diff --git a/src/l10n/en.json b/src/l10n/en.json index f26d146..cebe699 100644 --- a/src/l10n/en.json +++ b/src/l10n/en.json @@ -285,7 +285,11 @@ "plugins.cleanPro.cleanHeadlessBlocks": "Clean Headless Blocks", "plugins.cleanPro.docs.title": "Plugin Doc", "plugins.witcatInfiniteCanvas.title": "Infinite Canvas by Witcat", - "plugins.witcatInfiniteCanvas.open": "Enable Infinite Canvas" + "plugins.witcatInfiniteCanvas.open": "Enable Infinite Canvas", + "plugins.witcatInfiniteCanvas.originalImageUpload": "Original Image Upload", + "plugins.witcatInfiniteCanvas.unsupportedFileExtension": "Unsupported file extension", + "plugins.witcatInfiniteCanvas.fileReaderError": "File reader error", + "plugins.witcatInfiniteCanvas.imageLoadError": "Image load error", "plugins.findDefinition.title": "find definition of custom block", "plugins.findDefinition.description": "Right-click on a custom block in the code area and select【Jump To Definition】to jump to the definition location of that custom block.", "plugins.findDefinition.goToDefinition": "Jump To Definition", diff --git a/src/l10n/es.json b/src/l10n/es.json index 9948f0f..59ab190 100644 --- a/src/l10n/es.json +++ b/src/l10n/es.json @@ -263,5 +263,11 @@ "plugins.mobileCodeBatchSelect.zoomStage": "Zoom en el Escenario", "plugins.mobileCodeBatchSelect.zoomStage.description": "Pellizca en un área vacía en el espacio de trabajo del script para acercar o alejar.", "plugins.mobileCodeBatchSelect.boxSelect": "Selección de Bloques en Caja", - "plugins.mobileCodeBatchSelect.boxSelect.description": "Toca dos veces y mantén presionado con uno o dos dedos en un área vacía, luego arrastra para seleccionar varios bloques. Toca dos veces en un área vacía para deseleccionar." + "plugins.mobileCodeBatchSelect.boxSelect.description": "Toca dos veces y mantén presionado con uno o dos dedos en un área vacía, luego arrastra para seleccionar varios bloques. Toca dos veces en un área vacía para deseleccionar.", + "plugins.witcatInfiniteCanvas.open": "Habilitar Lienzo Infinito", + "plugins.witcatInfiniteCanvas.originalImageUpload": "Subir imagen original", + "plugins.witcatInfiniteCanvas.unsupportedFileExtension": "Extensión de archivo no admitida", + "plugins.witcatInfiniteCanvas.fileReaderError": "Error de lectura de archivo", + "plugins.witcatInfiniteCanvas.imageLoadError": "Error de carga de imagen", + "plugins.findDefinition.title": "buscar definición de bloque personalizado" } \ No newline at end of file diff --git a/src/l10n/ms.json b/src/l10n/ms.json index d079179..f795d1c 100644 --- a/src/l10n/ms.json +++ b/src/l10n/ms.json @@ -266,5 +266,11 @@ "plugins.mobileCodeBatchSelect.zoomStage": "Zum Kawasan Peringkat", "plugins.mobileCodeBatchSelect.zoomStage.description": "Cubit pada kawasan kosong dalam ruang kerja skrip untuk mengezum masuk dan keluar.", "plugins.mobileCodeBatchSelect.boxSelect": "Pilih Blok dengan Kotak", - "plugins.mobileCodeBatchSelect.boxSelect.description": "Ketuk dua kali dan tahan dengan satu atau dua jari di kawasan kosong, kemudian seret untuk memilih berbilang blok. Ketuk dua kali di kawasan kosong untuk nyahpilih." + "plugins.mobileCodeBatchSelect.boxSelect.description": "Ketuk dua kali dan tahan dengan satu atau dua jari di kawasan kosong, kemudian seret untuk memilih berbilang blok. Ketuk dua kali di kawasan kosong untuk nyahpilih.", + "plugins.witcatInfiniteCanvas.open": "Dayakan Kanvas Tak Terhingga", + "plugins.witcatInfiniteCanvas.originalImageUpload": "Muat Naik Imej Asal", + "plugins.witcatInfiniteCanvas.unsupportedFileExtension": "Sambungan fail tidak disokong", + "plugins.witcatInfiniteCanvas.fileReaderError": "Ralat pembaca fail", + "plugins.witcatInfiniteCanvas.imageLoadError": "Ralat memuatkan imej", + "plugins.findDefinition.title": "cari definisi blok tersuai" } \ No newline at end of file diff --git a/src/l10n/ru.json b/src/l10n/ru.json index e90d499..b38447a 100644 --- a/src/l10n/ru.json +++ b/src/l10n/ru.json @@ -263,5 +263,11 @@ "plugins.mobileCodeBatchSelect.zoomStage": "Масштабирование сцены", "plugins.mobileCodeBatchSelect.zoomStage.description": "Сожмите два пальца на пустой области в рабочем пространстве, чтобы изменить масштаб.", "plugins.mobileCodeBatchSelect.boxSelect": "Выбор блоков рамкой", - "plugins.mobileCodeBatchSelect.boxSelect.description": "Дважды коснитесь и удерживайте одним или двумя пальцами на пустой области, затем переместите палец, чтобы выделить несколько блоков. Дважды коснитесь пустой области, чтобы снять выделение." + "plugins.mobileCodeBatchSelect.boxSelect.description": "Дважды коснитесь и удерживайте одним или двумя пальцами на пустой области, затем переместите палец, чтобы выделить несколько блоков. Дважды коснитесь пустой области, чтобы снять выделение.", + "plugins.witcatInfiniteCanvas.open": "Включить бесконечный холст", + "plugins.witcatInfiniteCanvas.originalImageUpload": "Загрузить оригинальное изображение", + "plugins.witcatInfiniteCanvas.unsupportedFileExtension": "Неподдерживаемое расширение файла", + "plugins.witcatInfiniteCanvas.fileReaderError": "Ошибка чтения файла", + "plugins.witcatInfiniteCanvas.imageLoadError": "Ошибка загрузки изображения", + "plugins.findDefinition.title": "найти определение пользовательского блока" } \ No newline at end of file diff --git a/src/l10n/uk.json b/src/l10n/uk.json index 8459ff5..f6bde05 100644 --- a/src/l10n/uk.json +++ b/src/l10n/uk.json @@ -261,5 +261,11 @@ "plugins.mobileCodeBatchSelect.zoomStage": "Збільшення сцени", "plugins.mobileCodeBatchSelect.zoomStage.description": "Щипніть два пальці на порожній ділянці в робочій зоні, щоб змінити масштаб.", "plugins.mobileCodeBatchSelect.boxSelect": "Виділення блоків рамкою", - "plugins.mobileCodeBatchSelect.boxSelect.description": "Двічі торкніться і утримуйте одним або двома пальцями на порожній області, потім проведіть пальцем, щоб вибрати кілька блоків. Двічі торкніться порожньої області, щоб скасувати вибір." + "plugins.mobileCodeBatchSelect.boxSelect.description": "Двічі торкніться і утримуйте одним або двома пальцями на порожній області, потім проведіть пальцем, щоб вибрати кілька блоків. Двічі торкніться порожньої області, щоб скасувати вибір.", + "plugins.witcatInfiniteCanvas.open": "Увімкнути нескінченне полотно", + "plugins.witcatInfiniteCanvas.originalImageUpload": "Завантажити оригінальне зображення", + "plugins.witcatInfiniteCanvas.unsupportedFileExtension": "Непідтримуване розширення файлу", + "plugins.witcatInfiniteCanvas.fileReaderError": "Помилка читання файлу", + "plugins.witcatInfiniteCanvas.imageLoadError": "Помилка завантаження зображення", + "plugins.findDefinition.title": "знайти визначення користувацького блоку" } \ No newline at end of file diff --git a/src/l10n/zh-cn.json b/src/l10n/zh-cn.json index 70d5e43..8561b1a 100644 --- a/src/l10n/zh-cn.json +++ b/src/l10n/zh-cn.json @@ -286,7 +286,11 @@ "plugins.cleanPro.cleanHeadlessBlocks": "清理无头块", "plugins.cleanPro.docs.title": "插件文档", "plugins.witcatInfiniteCanvas.title": "白猫的无限画布", - "plugins.witcatInfiniteCanvas.open": "启用无限画布" + "plugins.witcatInfiniteCanvas.open": "启用无限画布", + "plugins.witcatInfiniteCanvas.originalImageUpload": "原图上传", + "plugins.witcatInfiniteCanvas.unsupportedFileExtension": "不支持的文件扩展名", + "plugins.witcatInfiniteCanvas.fileReaderError": "文件读取错误", + "plugins.witcatInfiniteCanvas.imageLoadError": "图片加载错误", "plugins.findDefinition.title": "查找自定义块的定义位置", "plugins.findDefinition.description": "在代码区右键单击自定义块,选择【跳转到定义】即可跳转到该自定义块的定义位置。", "plugins.findDefinition.goToDefinition": "跳转到定义", diff --git a/src/plugins/witcat-infinite-canvas/index.tsx b/src/plugins/witcat-infinite-canvas/index.tsx index a4cbdac..7afeda7 100644 --- a/src/plugins/witcat-infinite-canvas/index.tsx +++ b/src/plugins/witcat-infinite-canvas/index.tsx @@ -1,11 +1,73 @@ import * as React from "react"; +import toast from "react-hot-toast"; import Icon from "./icon"; +import { UploadIcon } from "./upload-icon"; declare const window: Window & { scratchPaintInfiniteCanvas: boolean; }; -const WitcatInfiniteCanvas: React.FC = ({ registerSettings, msg }) => { +const WitcatInfiniteCanvas: React.FC = ({ registerSettings, msg, utils, vm }) => { + const handleOriginalUpload = React.useCallback(() => { + if (!vm.editingTarget) return; + const input = document.createElement("input"); + input.type = "file"; + input.accept = ".svg, .png, .jpg, .jpeg"; + input.onchange = (e: Event) => { + const target = e.target as HTMLInputElement; + const file = target.files?.[0]; + if (!file) return; + + const ext = file.name.substring(file.name.lastIndexOf(".")).toLowerCase(); + if (![".svg", ".png", ".jpg", ".jpeg"].includes(ext)) { + toast.error(msg("plugins.witcatInfiniteCanvas.unsupportedFileExtension")); + return; + } + + const reader = new FileReader(); + reader.onerror = () => { + toast.error(msg("plugins.witcatInfiniteCanvas.fileReaderError")); + }; + + if (file.type === "image/svg+xml" || ext === ".svg") { + reader.onload = (readerEvent) => { + const content = readerEvent.target?.result as string; + if (!content) return; + utils.addCostumeToTarget( + content, + file.name, + "image/svg+xml", + vm.editingTarget.id + ); + }; + reader.readAsText(file); + } else { + reader.onload = (readerEvent) => { + const result = readerEvent.target?.result as string; + if (!result) return; + + const img = new Image(); + img.onload = () => { + const svg = ``; + utils.addCostumeToTarget( + svg, + file.name.replace(/\.[^/.]+$/, "") + ".svg", + "image/svg+xml", + vm.editingTarget.id + ); + }; + img.onerror = () => { + toast.error(msg("plugins.witcatInfiniteCanvas.imageLoadError")); + }; + img.src = result; + }; + reader.readAsDataURL(file); + } + input.remove(); + }; + input.click(); + }, [utils, vm]); + React.useEffect(() => { const register = registerSettings( msg("plugins.witcatInfiniteCanvas.title"), @@ -24,16 +86,37 @@ const WitcatInfiniteCanvas: React.FC = ({ registerSettings, msg } window.scratchPaintInfiniteCanvas = v; }, }, + { + key: "originalImageUpload", + type: "switch", + label: msg("plugins.witcatInfiniteCanvas.originalImageUpload"), + value: false, + onChange(v: boolean) { + if (v) { + utils.expandCostumeMenuItems([ + { + id: "originalImageUpload", + title: msg("plugins.witcatInfiniteCanvas.originalImageUpload"), + img: UploadIcon, + onClick: handleOriginalUpload, + }, + ]); + } else { + utils.removeCostumeMenuItems(["originalImageUpload"]); + } + }, + }, ], }, ], - , + ); return () => { window.scratchPaintInfiniteCanvas = false; + utils.removeCostumeMenuItems(["originalImageUpload"]); register.dispose(); }; - }, [registerSettings, msg]); + }, [registerSettings, msg, utils, handleOriginalUpload]); return null; }; diff --git a/src/plugins/witcat-infinite-canvas/upload-icon.ts b/src/plugins/witcat-infinite-canvas/upload-icon.ts new file mode 100644 index 0000000..0570948 --- /dev/null +++ b/src/plugins/witcat-infinite-canvas/upload-icon.ts @@ -0,0 +1 @@ +export const UploadIcon = `data:image/svg+xml;utf8,${encodeURIComponent(``)}`; diff --git a/webpackDevServer.config.js b/webpackDevServer.config.js index 55ba3d2..39e6cbd 100644 --- a/webpackDevServer.config.js +++ b/webpackDevServer.config.js @@ -114,4 +114,4 @@ module.exports = { }), ], target: ["web", "es6"], -}; +}; \ No newline at end of file