aboutsummaryrefslogtreecommitdiff
path: root/bundle/src/main.js
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-21 11:02:26 +0000
committer_ov4 <ov4@ov4.org>2026-10-21 11:02:26 +0000
commit5a254e2ab4741bc88c373ae1673c2ad9a7e6a588 (patch)
treec26f6fb9186ac4c8d121349909689cd045f9d11f /bundle/src/main.js
parentc17775323baf93e0d9bd44a6f83cd32a986b6fcb (diff)
feat(bundle): add the Tauri editor with shared Dark+ language support
Add a single-document Monaco editor, native file dialogs, save handling and prefilled input/output panels. Reuse the plugin's grammar and editor core. Verify frontend behavior and compile the desktop app on Linux. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'bundle/src/main.js')
-rw-r--r--bundle/src/main.js90
1 files changed, 90 insertions, 0 deletions
diff --git a/bundle/src/main.js b/bundle/src/main.js
new file mode 100644
index 0000000..0d3a010
--- /dev/null
+++ b/bundle/src/main.js
@@ -0,0 +1,90 @@
+import { invoke } from '@tauri-apps/api/core';
+import { getCurrentWindow } from '@tauri-apps/api/window';
+import { open, save, message } from '@tauri-apps/plugin-dialog';
+import { createEditor } from './editor.js';
+import './style.css';
+
+const status = document.querySelector('#status');
+const filename = document.querySelector('#filename');
+const window = getCurrentWindow();
+const filters = [{ name: 'Pseudocode', extensions: ['cps'] }];
+let path = null;
+let savedText = '';
+let busy = false;
+let closing = false;
+let editor;
+
+function dirty() { return editor.getValue() !== savedText; }
+function refreshTitle() {
+ const name = path ? path.split(/[\\/]/).pop() : 'Untitled.cps';
+ filename.textContent = `${dirty() ? '* ' : ''}${name}`;
+ filename.title = path || name;
+ window.setTitle(`${dirty() ? '* ' : ''}${name} — CPSC Bundle`);
+}
+function report(error) { status.textContent = String(error); }
+async function saveFile() {
+ const target = path || await save({ filters, defaultPath: 'Untitled.cps' });
+ if (!target) return false;
+ const text = editor.getValue();
+ await invoke('write_file', { path: target, text });
+ path = target;
+ savedText = text;
+ refreshTitle();
+ status.textContent = 'Saved';
+ return true;
+}
+async function canReplace() {
+ if (!dirty()) return true;
+ const answer = await message('Save changes?', {
+ title: 'CPSC Bundle', kind: 'warning',
+ buttons: { yes: 'Save', no: 'Discard', cancel: 'Cancel' }
+ });
+ if (answer === 'Cancel') return false;
+ return answer === 'Discard' || await saveFile();
+}
+async function action(callback) {
+ if (busy) return;
+ busy = true;
+ try { await callback(); } catch (error) { report(error); } finally { busy = false; }
+}
+
+try {
+ editor = await createEditor(document.querySelector('#editor'));
+ editor.onDidChangeModelContent(refreshTitle);
+ document.querySelector('#save').onclick = () => action(saveFile);
+ document.querySelector('#new').onclick = () => action(async () => {
+ if (!await canReplace()) return;
+ path = null;
+ savedText = '';
+ editor.setValue('');
+ refreshTitle();
+ status.textContent = 'Ready';
+ });
+ document.querySelector('#open').onclick = () => action(async () => {
+ if (!await canReplace()) return;
+ const target = await open({ filters, multiple: false });
+ if (!target) return;
+ const text = await invoke('read_file', { path: target });
+ path = target;
+ savedText = text;
+ editor.setValue(text);
+ refreshTitle();
+ status.textContent = 'Ready';
+ });
+ document.addEventListener('keydown', event => {
+ if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 's') {
+ event.preventDefault();
+ action(saveFile);
+ }
+ });
+ await window.onCloseRequested(async event => {
+ if (closing) return;
+ event.preventDefault();
+ await action(async () => {
+ if (!await canReplace()) return;
+ closing = true;
+ await window.close();
+ });
+ });
+ editor.focus();
+} catch (error) { report(error); }