diff --git a/README.md b/README.md index ca8f6bd5..25ef20a2 100644 --- a/README.md +++ b/README.md @@ -67,13 +67,16 @@ Current team: - Pierre Weill-Tessier (King's College London) Other contributors: +- Tobias Kohn (TigerPython parser) - Babis Kyfonidis - Zak Singh - Aleksandr Voronstov Translators: -- Greek: Babis Kyfonidis +- Chinese: Rongkun Liu - French: Pierre Weill-Tessier +- German: Michael Kölling +- Greek: Babis Kyfonidis - Spanish: Emma Dodoo and Nolgie Oquendo-Colon Roadmap @@ -81,7 +84,6 @@ Roadmap Strype is currently under very active development. A rough roadmap of some major planned features (beyond the general bugfixing and polishing): - - Improved editor features (including copy-as-image): Q4 2024 - Add class frames (for object-oriented programming): Q1 2025 - Add a graphics and sound API: Q1 2025 - Add support for third-party library import: Q2 2025 diff --git a/package.json b/package.json index 0d20c2ae..426da37e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "strype-editor", - "version": "1.0.0", + "version": "2024.12.13", "private": true, "scripts": { "serve": "vue-cli-service serve --port 8081", diff --git a/src/components/AddFrameCommand.vue b/src/components/AddFrameCommand.vue index 577deadd..316131ef 100644 --- a/src/components/AddFrameCommand.vue +++ b/src/components/AddFrameCommand.vue @@ -66,7 +66,6 @@ export default Vue.extend({ color: rgb(180, 180, 180); } - .frame-cmd-btn { margin-right: 5px; cursor: pointer; @@ -84,5 +83,6 @@ export default Vue.extend({ .frame-cmd-btn:disabled { cursor: default; + color: rgb(180, 180, 180); } diff --git a/src/components/Frame.vue b/src/components/Frame.vue index cf92cc40..a454f3a5 100644 --- a/src/components/Frame.vue +++ b/src/components/Frame.vue @@ -91,7 +91,7 @@ import CaretContainer from "@/components/CaretContainer.vue"; import { useStore } from "@/store/store"; import { DefaultFramesDefinition, CaretPosition, CurrentFrame, NavigationPosition, AllFrameTypesIdentifier, Position, PythonExecRunningState, FrameContextMenuActionName } from "@/types/types"; import VueContext, {VueContextConstructor} from "vue-context"; -import { getAboveFrameCaretPosition, getAllChildrenAndJointFramesIds, getLastSibling, getParent, getParentOrJointParent, isFramePartOfJointStructure, isLastInParent } from "@/helpers/storeMethods"; +import { getAboveFrameCaretPosition, getAllChildrenAndJointFramesIds, getLastSibling, getNextSibling, getParent, getParentOrJointParent, isFramePartOfJointStructure, isLastInParent } from "@/helpers/storeMethods"; import { CustomEventTypes, getFrameBodyUID, getFrameContextMenuUID, getFrameHeaderUID, getFrameUID, isIdAFrameId, getFrameBodyRef, getJointFramesRef, getCaretContainerRef, setContextMenuEventClientXY, adjustContextMenuPosition, getActiveContextMenu, notifyDragStarted, getCaretUID, getHTML2CanvasFramesSelectionCropOptions } from "@/helpers/editor"; import { mapStores } from "pinia"; import { BPopover } from "bootstrap-vue"; @@ -1059,12 +1059,23 @@ export default Vue.extend({ delete(): void { if(this.isPartOfSelection){ - //for deleting a selection, we don't care if we simulate "delete" or "backspace" as they behave the same + // For deleting a selection, we don't care if we simulate "delete" or "backspace" as they behave the same this.appStore.deleteFrames("Delete"); } else{ - //when deleting the specific frame, we place the caret below and simulate "backspace" - this.appStore.setCurrentFrame({id: this.frameId, caretPosition: CaretPosition.below}); + // When deleting the specific frame, we place the caret below and simulate "backspace" + // (special case for joint frames: we go inside the body's next joint sibling, or below root if no next joint sibling) + const newCurrentFrame = {id: this.frameId, caretPosition: CaretPosition.below}; + if(this.isJointFrame) { + if(isLastInParent(this.frameId)){ + newCurrentFrame.id = getParentOrJointParent(this.frameId); + } + else{ + newCurrentFrame.id = getNextSibling(this.frameId); + newCurrentFrame.caretPosition = CaretPosition.body; + } + } + this.appStore.setCurrentFrame(newCurrentFrame); this.appStore.deleteFrames("Backspace"); } }, diff --git a/src/localisation/zh/zh_main.json b/src/localisation/zh/zh_main.json new file mode 100644 index 00000000..c397c1a7 --- /dev/null +++ b/src/localisation/zh/zh_main.json @@ -0,0 +1,155 @@ +{ + "localeName": "中文", + "defaultProjName": "我的项目", + "strypeFileDesc": "Strype项目文档", + "pythonFileDesc": "Python 代码文档", + "frame": { + "defaultText": { + "condition": "条件 ", + "list": "列表 ", + "identifier": "标识符 ", + "expression": "表达式 ", + "value": "值 ", + "variable": "变量 ", + "funcCall": "函数名称", + "simpleFuncCall": "函数()", + "exception": "异常 ", + "name": "名称 ", + "parameters": "参数 ", + "module": "模块 ", + "modulePart": "函数/类 ", + "comment": "您的评论 " + }, + "funcdef_desc": "函数定义", + "varassign_desc": "赋值", + "funccall_desc": "函数调用", + "blank_desc": "空白行", + "comment_desc": "评论" + }, + "messageBannerMessage": { + "noUndo": "没有可用的撤消操作。", + "noRedo": "没有可用的重做操作。", + "uploadEditorFileError": "加载文件时出错:{errorMsg}。您的代码未更改。", + "uploadEditorFileNotSupported": "不支持此文件格式。预期的文件扩展名:{list}。您的代码未更改。", + "forbiddenFramePaste": "无法粘贴所选框架,因为它会导致非法代码。", + "functionFrameCantDelete": "当内容不为空时,无法删除函数定义框架。", + "gdriveConnectToSaveFailed": "无法连接到Google Drive 。与 Google Drive 的同步已停止,最新更改可能未保存。", + "gdriveCantCreateStrypeFolder": "Strype 无法在 Google Drive 中创建默认的 \"Strype\" 文件夹。", + "invalidPythonParseImport": "导入的 Python 代码无效: {errorMsg}", + "invalidPythonParsePaste": "粘贴的 Python 代码无效: {errorMsg}", + "wrongPythonStructCopied": "无法在一次粘贴中粘贴 else/elif/except/finally 和其他内容", + "incompatiblePythonStrypeSection": "无法将 Python 代码放置在目标 Strype 部分中" + }, + "buttonLabel": { + "undo": "撤消", + "yes": "是", + "no": "否", + "ok": "确定", + "cancel": "取消", + "saveDiffLocation": "保存到其他地方", + "overwriteProject": "替换", + "saveProjectCopy": "选择新名称", + "spaceBar": "空格", + "yesSign" : "是,请登录", + "noContinueWithout":"否,无需登录继续" + }, + "errorMessage": { + "wrongDataFormat": "无法读取数据格式", + "stateDataIntegrity": "数据似乎已损坏", + "dataNotObject": "数据未描述对象", + "stateWrongPlatform": "文件由不兼容的 Strype 版本创建", + "stateWrongFrameTypeName": "读取框架类型时出错", + "emptyEditableSlot": "插槽不能为空", + "errorTitle": "错误", + "pastFrameErrTitle": "上次运行时出现运行时错误", + "errorUserDefinedFuncMsg": "用户定义函数\n此函数定义包含错误", + "errorUserDefinedVarMsg": "用户定义变量\n此变量的一个赋值包含错误", + "EOFError": "此框架不能为空。", + "GDriveSaveFailed": "无法保存到 Google Drive。您的最新更改可能尚未保存。自动保存已关闭。", + "fileNameError": "项目/文件名只能包含字母、数字、空格、连字符或括号", + "gdriveConnectionSaveToLoadProjFailed": "尝试保存当前项目时无法连接到 Google Drive。请尝试重新登录以打开另一个项目。", + "gdriveConnectionSaveToUnloadPageFailed": "尝试保存当前项目时无法连接到 Google Drive。请在离开Strype前尝试重新登录以保存当前项目 。", + "gdrivePermissionsNotMet": "Strype 至少需要一项访问权限才能使用 Google Drive。", + "gdriveReadOnly": "此文件为只读。自动保存已关闭。", + "gdriveWrongFile": "请选择一个 Strype 文件(扩展名为 \".spy\")。", + "unexpectedCharsPython": "意外字符" + }, + "appMessage": { + "editorFileUpload": "正在将文件上传到编辑器...", + "editorConfirmChangeCode": "当前编辑器的内容将永久丢失。", + "editorFileUploadWrongVersion": "此代码由其他版本的编辑器生成。\n导入可能会导致错误。\n\n您还想继续吗?", + "importsContainer": "导入:", + "funcDefsContainer": "函数定义:", + "mainContainer": "我的代码:", + "preCompiledErrorNeedFix": "请先修复现有错误。", + "noWebUSB": "此浏览器不支持 webUSB 连接。请使用 Google Chrome 等浏览器。", + "emptyCodeError": "代码不能为空。", + "loadToTarget": "打开自", + "saveToTarget": "保存至", + "targetFS": "此设备", + "fileName": "文件名:", + "gdriveFileAlreadyExists": "Drive 位置中已存在同名项目。\n是否要替换它?", + "gdriveLockedFileAlreadyExists": "此项目已锁定在 Drive 位置。\n无法使用此名称保存。", + "autosaveGDrive": "已保存", + "autoSaveGDriveTooltip": "项目将每 {freq} 分钟自动保存到 Google Drive。", + "selectFolder": "选择一个文件夹", + "selectStrypeFile": "选择一个 Strype 项目", + "gdriveLocation": "文件夹:", + "gdriveAllStrypeFiles": "所有 Strype 文件", + "gdriveTab": "我的 Drive", + "editorErrors": "错误", + "resyncToGDAtStartup": "上次编辑此项目时,您正在保存到 Google Drive。\n是否要重新连接到 Google Drive 以同步您的更改?\n如果不这样做,您将无法获得来自 Google Drive 的最新版本,并且您的更改将不会保存回那里。" + }, + "contextMenu": { + "ctrl": "控制", + "cut": "剪切", + "copy": "复制", + "downloadAsImg": "下载为图像", + "duplicate": "复制于下方", + "paste": "粘贴", + "pasteAbove": "粘贴到上方", + "pasteBelow": "粘贴到下方", + "pasteBelowJointRoot": "粘贴到根目录下", + "enable": "启用", + "disable": "禁用", + "undo": "撤消", + "redo": "重做", + "delete": "删除", + "deleteOuter": "删除外部", + "insert": "插入" + }, + "appMenu": { + "downloadHex": "转换为 Hex 文件", + "downloadPython": "转换为 Python 文件", + "loadProject": "打开", + "saveProject": "保存", + "saveAsProject": "另存为...", + "resetProject": "新项目", + "resetProjectTooltip": "使用 Strype 默认项目重置编辑器", + "prefs": "首选项", + "lang": "语言:", + "homepage": "Strype 主页", + "version": "版本" + }, + "autoCompletion": { + "myFunctions": "我的函数", + "myVariables": "我的变量", + "importedModules": "导入的模块", + "invalidState": "没有可用的完成项目", + "noDocumentation": "没有可用的文档" + }, + "commandTabs": { + "0": "添加框架" + }, + "PEA": { + "run": "运行", + "stop": "停止", + "stopping": "正在停止...", + "runtimeErrorConsole": "运行错误", + "expand": "展开", + "collapse": "折叠", + "console": "控制台", + "TurtleGraphics": "海龟绘图", + "importTurtle": "导入模块'turtle'以使用海龟绘图。" + } +}