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'以使用海龟绘图。"
+ }
+}