regen.mde 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +16 -16
  2. package/README.md +2 -1
  3. package/bin/build-corpus-editor.js +83 -83
  4. package/bin/build-corpus.js +41 -41
  5. package/bin/regen-mdeditor-install.js +27 -27
  6. package/bin/regen-mdeditor-uninstall.js +19 -19
  7. package/bin/validate-katex.js +93 -93
  8. package/desktop/BuildCorpusEditor/BuildCorpusEditor.csproj +22 -22
  9. package/desktop/BuildCorpusEditor/EditorForm.cs +48 -0
  10. package/desktop/BuildCorpusEditor/app.manifest +16 -16
  11. package/dist/release/{regen-mde-0.8.0-win-x64.zip → regen-mde-0.6.1-win-x64.zip} +0 -0
  12. package/dist/release/regen-mde-0.8.2-win-x64.zip +0 -0
  13. package/dist/windows-editor/BuildCorpusEditor.dll +0 -0
  14. package/dist/windows-editor/BuildCorpusEditor.exe +0 -0
  15. package/dist/windows-editor/BuildCorpusEditor.pdb +0 -0
  16. package/dist/windows-editor/wwwroot/assets/{index-C_VxJk4k.js → index-BB0sbZaD.js} +107 -107
  17. package/dist/windows-editor/wwwroot/assets/index-CtOv7qsC.css +1 -0
  18. package/dist/windows-editor/wwwroot/index.html +22 -22
  19. package/editor-web/index.html +21 -21
  20. package/editor-web/src/main.jsx +91 -53
  21. package/editor-web/src/styles.css +65 -1
  22. package/editor-web/vite.config.js +13 -13
  23. package/examples/build-corpus.config.example.json +21 -21
  24. package/installer/install-regen-mde.ps1 +214 -214
  25. package/installer/regen-mde.nsi +81 -81
  26. package/package.json +1 -1
  27. package/pyproject.toml +1 -1
  28. package/scripts/build-windows-editor.ps1 +47 -47
  29. package/scripts/package-windows-editor.ps1 +90 -90
  30. package/scripts/run-corpus.ps1 +28 -28
  31. package/scripts/run-editor-implementation-plane.ps1 +226 -226
  32. package/scripts/run-required-tests.ps1 +98 -98
  33. package/scripts/run-smoke.ps1 +28 -28
  34. package/src/build_corpus/__init__.py +3 -3
  35. package/src/build_corpus/docx_exporter.py +10 -4
  36. package/src/build_corpus/equations.py +1345 -1345
  37. package/src/build_corpus/templates/__init__.py +1 -1
  38. package/src/build_corpus/validate_assets.py +46 -46
  39. package/tools/audit_corpus.py +203 -203
  40. package/tools/collect_microsoft_word_templates.py +228 -228
  41. package/tools/collect_online_docx_corpus.py +272 -272
  42. package/tools/collect_online_pptx_corpus.py +252 -252
  43. package/tools/compare_pptx_inputs_outputs.py +87 -87
  44. package/tools/roundtrip_docx_corpus.py +171 -171
  45. package/dist/windows-editor/wwwroot/assets/index-Wt9zSjIw.css +0 -1
@@ -0,0 +1 @@
1
+ :root{color-scheme:light;font-family:Segoe UI,Inter,system-ui,sans-serif;background:#fbf8f0;color:#192028;--ink: oklch(24% .018 250);--muted: oklch(47% .022 250);--paper: oklch(98% .011 92);--panel: oklch(95% .014 92);--line: oklch(82% .018 92);--soft: oklch(91% .022 92);--accent: oklch(56% .135 176);--accent-dark: oklch(36% .09 176);--warning: oklch(52% .12 23);--violet: oklch(60% .11 284);--source: oklch(99% .005 92);--source-ink: oklch(27% .018 250);--shadow: 0 24px 70px rgba(34, 38, 46, .18);--button-bg: oklch(99% .006 92);--button-hover: oklch(94% .034 176);--topbar-bg: rgba(252, 249, 240, .94);--panel-bg: rgba(248, 245, 236, .96);--card-bg: oklch(99% .006 92);--page-bg: oklch(99% .005 92);--page-ink: oklch(27% .018 250);--rail-bg: oklch(28% .026 250);--rail-item-bg: oklch(37% .04 250);--grid-line-strong: rgba(41, 143, 132, .08);--grid-line-soft: rgba(41, 143, 132, .05);--warning-bg: oklch(93% .044 72);--warning-border: oklch(74% .075 72);--warning-ink: oklch(35% .075 72);--bubble-bg: oklch(24% .022 250);--bubble-line: oklch(35% .025 250);--bubble-ink: oklch(94% .01 92);--table-head: oklch(92% .025 92)}[data-theme=dark]{color-scheme:dark;--ink: oklch(91% .014 250);--muted: oklch(68% .018 250);--paper: oklch(20% .018 250);--panel: oklch(26% .018 250);--line: oklch(38% .022 250);--soft: oklch(16% .018 250);--accent: oklch(65% .13 176);--accent-dark: oklch(88% .058 176);--warning: oklch(71% .14 68);--violet: oklch(71% .1 284);--source: oklch(96% .008 92);--source-ink: oklch(25% .018 250);--shadow: 0 24px 80px rgba(0, 0, 0, .46);--button-bg: oklch(27% .018 250);--button-hover: oklch(32% .038 176);--topbar-bg: rgba(25, 29, 36, .96);--panel-bg: rgba(29, 33, 40, .96);--card-bg: oklch(24% .018 250);--page-bg: oklch(96% .008 92);--page-ink: oklch(25% .018 250);--rail-bg: oklch(13% .02 250);--rail-item-bg: oklch(24% .03 250);--grid-line-strong: rgba(86, 205, 190, .08);--grid-line-soft: rgba(86, 205, 190, .045);--warning-bg: oklch(25% .044 68);--warning-border: oklch(48% .11 68);--warning-ink: oklch(84% .09 68);--bubble-bg: oklch(91% .012 92);--bubble-line: oklch(72% .02 92);--bubble-ink: oklch(20% .022 250);--table-head: oklch(90% .018 92)}*{box-sizing:border-box}body{margin:0;min-width:920px}button{min-height:28px;border:1px solid var(--line);border-radius:5px;padding:4px 9px;background:var(--button-bg);color:var(--ink);font:600 12px/1 Segoe UI,system-ui,sans-serif;letter-spacing:0;cursor:pointer}button:hover:not(:disabled),button.active{border-color:var(--accent);background:var(--button-hover);color:var(--accent-dark)}button:disabled{cursor:default;opacity:.42}button.primary{background:var(--accent);border-color:#006453;border-color:oklch(45% .11 176);color:#f2fbf8}button.ghost{background:transparent}main.app-shell{width:100vw;height:100vh;min-height:720px;display:grid;grid-template-columns:58px 260px minmax(560px,1fr) 320px;grid-template-rows:48px 1fr 30px;overflow:hidden;background:linear-gradient(90deg,var(--grid-line-strong) 0 1px,transparent 1px 72px),linear-gradient(var(--grid-line-soft) 0 1px,transparent 1px 72px),var(--paper)}main.app-shell.left-collapsed{grid-template-columns:58px 44px minmax(560px,1fr) 320px}main.app-shell.right-collapsed{grid-template-columns:58px 260px minmax(560px,1fr) 44px}main.app-shell.left-collapsed.right-collapsed{grid-template-columns:58px 44px minmax(560px,1fr) 44px}.brand-rail{grid-row:1 / 4;border-right:1px solid var(--line);background:var(--rail-bg);color:#e7e4dc;display:grid;grid-template-rows:64px repeat(5,42px) 1fr 42px;place-items:center}.mark{width:32px;height:32px;border-radius:8px;display:grid;place-items:center;background:var(--accent);color:#f2fbf8;font-weight:800}.rail-item{width:34px;height:34px;border-radius:7px;display:grid;place-items:center;color:#c7c4bc;font-size:12px;font-weight:700}.rail-item.active{background:var(--rail-item-bg);color:#fbf8f1}.topbar{grid-column:2 / 5;border-bottom:1px solid var(--line);background:var(--topbar-bg);display:flex;align-items:center;justify-content:space-between;gap:16px;padding:0 14px}.file-title{min-width:0;display:flex;align-items:baseline;gap:10px}.file-title strong{font-size:15px}.file-title span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--muted);font:12px/1.2 Consolas,Cascadia Mono,monospace}.actions{display:flex;align-items:center;gap:6px}.side-panel{border-color:var(--line);background:var(--panel-bg);padding:14px;overflow:auto}.side-panel.collapsed{overflow:hidden;padding:8px 5px}.left-panel{grid-row:2 / 3;grid-column:2 / 3;border-right:1px solid var(--line)}.right-panel{grid-row:2 / 3;grid-column:4 / 5;border-left:1px solid var(--line)}.panel-chrome{min-height:28px;display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:10px}.left-panel .panel-chrome{flex-direction:row-reverse}.panel-toggle{width:28px;min-width:28px;height:28px;min-height:28px;display:grid;place-items:center;padding:0;font-size:17px;line-height:1}.side-panel.collapsed .panel-chrome{height:100%;min-height:0;flex-direction:column;justify-content:flex-start;margin:0}.side-panel.collapsed .panel-title{writing-mode:vertical-rl;transform:rotate(180deg);white-space:nowrap;margin:10px 0 0;max-height:calc(100vh - 150px);overflow:hidden;text-overflow:ellipsis}.side-panel.collapsed .panel-body{display:none}.panel-title{margin:0 0 10px;color:var(--muted);font-size:11px;font-weight:800;text-transform:uppercase}.source-card,.check{border:1px solid var(--line);border-radius:7px;background:var(--card-bg)}.source-card{padding:12px;margin-bottom:12px}.source-card strong{display:block;font-size:13px;margin-bottom:5px}.source-card p{margin:0;color:var(--muted);font-size:12px;line-height:1.4}.batch-option{display:flex;align-items:center;gap:8px;margin-top:10px;color:var(--text);font-size:12px;line-height:1.35}.format-flow,.checklist{display:grid;gap:8px;margin:14px 0}.flow-step{display:grid;grid-template-columns:32px 1fr auto;align-items:center;gap:9px;min-height:42px;padding:7px;border:1px solid var(--line);border-radius:7px;background:var(--panel)}.badge{height:26px;border-radius:5px;display:grid;place-items:center;background:#c0dfd6;color:#003d32;color:oklch(32% .07 176);font-weight:800;font-size:10px}.flow-step small{display:block;color:var(--muted);font-size:11px}.insert-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.insert-tile{min-height:58px;border:1px solid var(--line);border-radius:7px;padding:9px;background:var(--card-bg);font-size:12px;text-align:left}.insert-tile b{display:block;margin-bottom:5px}.workspace{grid-row:2 / 3;grid-column:3 / 4;min-width:0;min-height:0;display:grid;grid-template-rows:42px 1fr;overflow:hidden;background:var(--soft)}.mode-strip{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line);padding:0 14px}.segmented{display:inline-flex;gap:2px;padding:3px;border:1px solid var(--line);border-radius:7px;background:var(--card-bg)}.segmented button{border:0;min-height:24px;background:transparent}.segmented button.active{background:var(--ink);color:#fbf8f1}.canvas-wrap{min-height:0;height:100%;display:grid;grid-template-columns:minmax(360px,1fr) minmax(300px,.72fr);overflow:hidden}.workspace.markdown .canvas-wrap,.workspace.markdown.large .canvas-wrap{grid-template-columns:1fr}.workspace.split .canvas-wrap{grid-template-columns:minmax(360px,1fr) minmax(300px,.72fr)}.workspace.raw .canvas-wrap{grid-template-columns:1fr}.workspace.markdown .markdown-pane,.workspace.raw .page-area{display:none}.page-area{min-width:0;min-height:0;height:100%;overflow:auto;padding:26px 38px 60px}.page{position:relative;width:min(760px,100%);min-height:900px;margin:0 auto;padding:58px 64px;border:1px solid oklch(84% .014 92);border-radius:6px;background:var(--page-bg);box-shadow:var(--shadow)}.page-label{margin:0 0 18px;color:var(--warning);font-size:11px;font-weight:800;text-transform:uppercase}.rich .ProseMirror{min-height:720px;outline:none;color:var(--page-ink);line-height:1.62}.rich .ProseMirror h1,.rendered-markdown h1{margin:0 0 18px;max-width:14ch;font-size:42px;line-height:.98}.rich .ProseMirror h2,.rendered-markdown h2{margin-top:32px;font-size:25px}.rich .ProseMirror p,.rich .ProseMirror li,.rendered-markdown p,.rendered-markdown li{font-size:15px}.rich .ProseMirror blockquote,.rendered-markdown blockquote{margin:24px 0;padding:15px;border:1px solid oklch(78% .055 176);border-radius:7px;background:color-mix(in oklch,var(--accent) 14%,var(--page-bg))}.rich .ProseMirror table,.rendered-markdown table{width:100%;margin:22px 0;border-collapse:collapse;font-size:13px}.rich .ProseMirror th,.rich .ProseMirror td,.rendered-markdown th,.rendered-markdown td{border:1px solid oklch(78% .016 92);padding:8px;text-align:left}.rich .ProseMirror th,.rendered-markdown th{background:var(--table-head)}.rich img,.rendered-markdown img{max-width:100%;height:auto;border-radius:5px}.readonly-preview{color:var(--page-ink);line-height:1.62}.rendered-markdown{min-height:720px}.rendered-markdown pre{overflow:auto;padding:12px;border-radius:6px;background:color-mix(in oklch,var(--page-ink) 8%,var(--page-bg))}.rendered-markdown code{font-family:Consolas,Cascadia Mono,monospace}.markdown-pane{min-width:0;min-height:0;height:100%;overflow:auto;padding:18px;background:var(--source);color:var(--source-ink);border-left:1px solid var(--line)}.editable-markdown{display:flex;flex-direction:column;gap:10px}.markdown-toolbar{display:flex;flex-wrap:wrap;gap:4px;padding-bottom:8px;border-bottom:1px solid var(--line)}.markdown-toolbar button{min-width:30px;min-height:26px;border-color:var(--line);background:color-mix(in oklch,var(--page-ink) 4%,var(--source));color:var(--source-ink)}.source{width:100%;min-height:0;flex:1;border:0;outline:none;background:transparent;color:inherit;font:13px/1.55 Consolas,Cascadia Mono,monospace}.code-source,.code-source>div,.code-source [class*=cm-theme],.code-source .cm-editor,.code-source .cm-scroller{min-height:0;height:100%}.code-source .cm-editor{border-radius:6px;border:1px solid oklch(84% .014 92);background:var(--source);color:var(--source-ink)}.code-source .cm-scroller{background:var(--source);color:var(--source-ink);font-family:Consolas,Cascadia Mono,monospace;font-size:13px;line-height:1.55}.code-source .cm-content{color:var(--source-ink)}.code-source .cm-gutters{border-right:1px solid var(--line);background:var(--source);color:var(--muted)}.code-source .cm-lineNumbers .cm-gutterElement{min-width:26px;padding:0 6px 0 2px;color:var(--muted);font-size:10px;opacity:.52}.code-source .cm-activeLine,.code-source .cm-activeLineGutter{background:color-mix(in oklch,var(--accent) 8%,transparent)}.bubble-menu,.floating-menu{display:flex;align-items:center;gap:3px;padding:5px;border:1px solid var(--bubble-line);border-radius:8px;background:var(--bubble-bg);box-shadow:0 16px 32px #181c2438}.floating-menu{background:var(--card-bg);border-color:var(--line)}.bubble-menu button{min-width:28px;border-color:#3e4c5a;background:transparent;color:var(--bubble-ink)}.floating-menu button{min-width:28px;min-height:26px}.meter{height:8px;overflow:hidden;border-radius:999px;background:#d8d4c7;margin-top:10px}.meter span{display:block;height:100%;background:linear-gradient(90deg,var(--accent),var(--violet))}.check{display:grid;grid-template-columns:24px 1fr;gap:8px;align-items:start;padding:8px;font-size:12px}.dot{width:16px;height:16px;margin-top:1px;border-radius:999px;background:var(--accent)}.dot.warn{background:var(--warning)}.tool-group{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;margin-bottom:12px}.tool-group button{min-width:0}.statusbar{grid-column:2 / 5;border-top:1px solid var(--line);background:#f5f2e9;color:var(--muted);display:flex;align-items:center;justify-content:space-between;gap:20px;padding:0 12px;font-size:12px}.statusbar span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(max-width:840px){body{min-width:760px}main.app-shell{grid-template-columns:52px 1fr;grid-template-rows:48px 1fr 30px}.topbar,.statusbar{grid-column:2 / 3}.left-panel,.right-panel{display:none}.workspace{grid-column:2 / 3}.canvas-wrap{grid-template-columns:1fr}.workspace.split .canvas-wrap{grid-template-columns:minmax(340px,1fr) minmax(280px,.78fr)}}.compose-shell{display:grid;grid-template-rows:auto 1fr auto;height:100vh;width:100vw;background:#101214;color:#eef1f4;font-family:Segoe UI,Inter,system-ui,sans-serif}.compose-topbar,.compose-statusbar{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:12px 16px;background:#181b1f;border-bottom:1px solid #353b44}.compose-statusbar{border-bottom:0;border-top:1px solid #353b44;color:#9ca8b4;font-size:12.5px;gap:14px}.compose-statusbar span:first-child{flex:1 1 auto;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.compose-title{display:flex;flex-direction:column;gap:2px;min-width:0}.compose-title strong{font-size:16px}.compose-title span{font-size:12px;color:#9ca8b4;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.compose-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end}.compose-actions button{min-height:34px;border:1px solid #353b44;background:#20242a;color:#eef1f4;border-radius:6px;padding:0 12px;font:inherit;font-size:13px;cursor:pointer}.compose-actions button:hover{border-color:#9cc7ff}.compose-actions button.active{border-color:#65d0a4;color:#65d0a4}.compose-actions button.primary{background:#65d0a4;color:#06120d;border-color:#65d0a4;font-weight:650}.compose-actions button.primary:disabled{opacity:.6;cursor:default}.compose-editor-wrap{padding:14px;min-height:0;display:flex}.compose-editor-wrap.dragging{outline:3px solid #9cc7ff;outline-offset:-10px}.compose-editor{width:100%;height:100%;resize:none;border:1px solid #353b44;border-radius:8px;background:#0c0e10;color:#eef1f4;padding:16px;font:15px/1.55 Cascadia Code,Consolas,monospace;outline:none}.compose-editor:focus{border-color:#9cc7ff}
@@ -1,22 +1,22 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>regen-mde</title>
7
- <script>
8
- window.__REGEN_MDEDITOR_ERROR = "";
9
- window.addEventListener("error", function (event) {
10
- window.__REGEN_MDEDITOR_ERROR = event.message || "window error";
11
- });
12
- window.addEventListener("unhandledrejection", function (event) {
13
- window.__REGEN_MDEDITOR_ERROR = event.reason && event.reason.message ? event.reason.message : String(event.reason || "unhandled rejection");
14
- });
15
- </script>
16
- <script type="module" crossorigin src="./assets/index-C_VxJk4k.js"></script>
17
- <link rel="stylesheet" crossorigin href="./assets/index-Wt9zSjIw.css">
18
- </head>
19
- <body>
20
- <div id="root"></div>
21
- </body>
22
- </html>
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>regen-mde</title>
7
+ <script>
8
+ window.__REGEN_MDEDITOR_ERROR = "";
9
+ window.addEventListener("error", function (event) {
10
+ window.__REGEN_MDEDITOR_ERROR = event.message || "window error";
11
+ });
12
+ window.addEventListener("unhandledrejection", function (event) {
13
+ window.__REGEN_MDEDITOR_ERROR = event.reason && event.reason.message ? event.reason.message : String(event.reason || "unhandled rejection");
14
+ });
15
+ </script>
16
+ <script type="module" crossorigin src="./assets/index-BB0sbZaD.js"></script>
17
+ <link rel="stylesheet" crossorigin href="./assets/index-CtOv7qsC.css">
18
+ </head>
19
+ <body>
20
+ <div id="root"></div>
21
+ </body>
22
+ </html>
@@ -1,21 +1,21 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>regen-mde</title>
7
- <script>
8
- window.__REGEN_MDEDITOR_ERROR = "";
9
- window.addEventListener("error", function (event) {
10
- window.__REGEN_MDEDITOR_ERROR = event.message || "window error";
11
- });
12
- window.addEventListener("unhandledrejection", function (event) {
13
- window.__REGEN_MDEDITOR_ERROR = event.reason && event.reason.message ? event.reason.message : String(event.reason || "unhandled rejection");
14
- });
15
- </script>
16
- </head>
17
- <body>
18
- <div id="root"></div>
19
- <script type="module" src="/src/main.jsx"></script>
20
- </body>
21
- </html>
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>regen-mde</title>
7
+ <script>
8
+ window.__REGEN_MDEDITOR_ERROR = "";
9
+ window.addEventListener("error", function (event) {
10
+ window.__REGEN_MDEDITOR_ERROR = event.message || "window error";
11
+ });
12
+ window.addEventListener("unhandledrejection", function (event) {
13
+ window.__REGEN_MDEDITOR_ERROR = event.reason && event.reason.message ? event.reason.message : String(event.reason || "unhandled rejection");
14
+ });
15
+ </script>
16
+ </head>
17
+ <body>
18
+ <div id="root"></div>
19
+ <script type="module" src="/src/main.jsx"></script>
20
+ </body>
21
+ </html>
@@ -164,6 +164,8 @@ function App() {
164
164
  const [dirty, setDirty] = React.useState(false);
165
165
  const [largeFile, setLargeFile] = React.useState(false);
166
166
  const [moveSources, setMoveSources] = React.useState(false);
167
+ const [leftPanelCollapsed, setLeftPanelCollapsed] = React.useState(() => window.localStorage?.getItem("build-corpus-editor-left-panel") === "collapsed");
168
+ const [rightPanelCollapsed, setRightPanelCollapsed] = React.useState(() => window.localStorage?.getItem("build-corpus-editor-right-panel") === "collapsed");
167
169
  const sourceViewRef = React.useRef(null);
168
170
  const pageAreaRef = React.useRef(null);
169
171
  const scrollSyncRef = React.useRef(false);
@@ -225,6 +227,14 @@ function App() {
225
227
  window.localStorage?.setItem("build-corpus-editor-theme", theme);
226
228
  }, [theme]);
227
229
 
230
+ React.useEffect(() => {
231
+ window.localStorage?.setItem("build-corpus-editor-left-panel", leftPanelCollapsed ? "collapsed" : "open");
232
+ }, [leftPanelCollapsed]);
233
+
234
+ React.useEffect(() => {
235
+ window.localStorage?.setItem("build-corpus-editor-right-panel", rightPanelCollapsed ? "collapsed" : "open");
236
+ }, [rightPanelCollapsed]);
237
+
228
238
  async function chooseOpen() {
229
239
  try {
230
240
  const result = await bridge.call("chooseOpen");
@@ -527,7 +537,7 @@ function App() {
527
537
  const codeMirrorTheme = "light";
528
538
 
529
539
  return (
530
- <main className="app-shell" data-theme={theme}>
540
+ <main className={`app-shell${leftPanelCollapsed ? " left-collapsed" : ""}${rightPanelCollapsed ? " right-collapsed" : ""}`} data-theme={theme}>
531
541
  <aside className="brand-rail" aria-label="Primary tools">
532
542
  <div className="mark">BC</div>
533
543
  <div className="rail-item active">Ed</div>
@@ -556,37 +566,51 @@ function App() {
556
566
  </div>
557
567
  </header>
558
568
 
559
- <section className="side-panel left-panel">
560
- <h2 className="panel-title">Document Flow</h2>
561
- <div className="source-card">
562
- <strong>{sourceText}</strong>
563
- <p>{dirty ? "The Markdown draft has unsaved changes." : "The Markdown draft is in sync with its save target."}</p>
564
- <label className="batch-option"><input type="checkbox" checked={moveSources} onChange={(event) => setMoveSources(event.target.checked)} /> Move processed files to sources</label>
569
+ <section className={`side-panel left-panel${leftPanelCollapsed ? " collapsed" : ""}`}>
570
+ <div className="panel-chrome">
571
+ <button
572
+ type="button"
573
+ className="panel-toggle"
574
+ aria-label={leftPanelCollapsed ? "Expand Document Flow panel" : "Collapse Document Flow panel"}
575
+ aria-expanded={!leftPanelCollapsed}
576
+ onClick={() => setLeftPanelCollapsed((collapsed) => !collapsed)}
577
+ title={leftPanelCollapsed ? "Expand Document Flow" : "Collapse Document Flow"}
578
+ >
579
+ <span aria-hidden="true">≡</span>
580
+ </button>
581
+ <h2 className="panel-title">Document Flow</h2>
565
582
  </div>
566
- <div className="format-flow">
567
- <div className="flow-step">
568
- <div className="badge">{sourceBadge}</div>
569
- <div><b>Original</b><small>{sourcePath || "Choose a file to begin"}</small></div>
570
- <small>{sourcePath ? "loaded" : "empty"}</small>
571
- </div>
572
- <div className="flow-step">
573
- <div className="badge">MD</div>
574
- <div><b>Working draft</b><small>{draftText}</small></div>
575
- <small>{dirty ? "dirty" : "saved"}</small>
583
+ <div className="panel-body">
584
+ <div className="source-card">
585
+ <strong>{sourceText}</strong>
586
+ <p>{dirty ? "The Markdown draft has unsaved changes." : "The Markdown draft is in sync with its save target."}</p>
587
+ <label className="batch-option"><input type="checkbox" checked={moveSources} onChange={(event) => setMoveSources(event.target.checked)} /> Move processed files to sources</label>
576
588
  </div>
577
- <div className="flow-step">
578
- <div className="badge">DOCX</div>
579
- <div><b>Word export</b><small>{exportText}</small></div>
580
- <small>{exportPath ? "done" : "ready"}</small>
589
+ <div className="format-flow">
590
+ <div className="flow-step">
591
+ <div className="badge">{sourceBadge}</div>
592
+ <div><b>Original</b><small>{sourcePath || "Choose a file to begin"}</small></div>
593
+ <small>{sourcePath ? "loaded" : "empty"}</small>
594
+ </div>
595
+ <div className="flow-step">
596
+ <div className="badge">MD</div>
597
+ <div><b>Working draft</b><small>{draftText}</small></div>
598
+ <small>{dirty ? "dirty" : "saved"}</small>
599
+ </div>
600
+ <div className="flow-step">
601
+ <div className="badge">DOCX</div>
602
+ <div><b>Word export</b><small>{exportText}</small></div>
603
+ <small>{exportPath ? "done" : "ready"}</small>
604
+ </div>
581
605
  </div>
582
- </div>
583
606
 
584
- <h2 className="panel-title">Insert</h2>
585
- <div className="insert-grid">
586
- <button className="insert-tile" disabled={!canUseRich} onClick={() => editor?.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()}><b>Table</b>Rows, columns, headers</button>
587
- <button className="insert-tile" disabled={!canUseRich} onClick={insertImageFile}><b>Image</b>File with alt text</button>
588
- <button className="insert-tile" disabled={!canUseRich} onClick={() => editor?.chain().focus().toggleBlockquote().run()}><b>Callout</b>Note or quotation</button>
589
- <button className="insert-tile" disabled={!canUseRich} onClick={() => editor?.chain().focus().toggleTaskList().run()}><b>Task</b>Checklist block</button>
607
+ <h2 className="panel-title">Insert</h2>
608
+ <div className="insert-grid">
609
+ <button className="insert-tile" disabled={!canUseRich} onClick={() => editor?.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()}><b>Table</b>Rows, columns, headers</button>
610
+ <button className="insert-tile" disabled={!canUseRich} onClick={insertImageFile}><b>Image</b>File with alt text</button>
611
+ <button className="insert-tile" disabled={!canUseRich} onClick={() => editor?.chain().focus().toggleBlockquote().run()}><b>Callout</b>Note or quotation</button>
612
+ <button className="insert-tile" disabled={!canUseRich} onClick={() => editor?.chain().focus().toggleTaskList().run()}><b>Task</b>Checklist block</button>
613
+ </div>
590
614
  </div>
591
615
  </section>
592
616
 
@@ -676,35 +700,49 @@ function App() {
676
700
  </div>
677
701
  </section>
678
702
 
679
- <aside className="side-panel right-panel">
680
- <h2 className="panel-title">Output Health</h2>
681
- <div className="source-card">
682
- <strong>Round-trip confidence</strong>
683
- <p>Tables, images, links, and headings are tracked as conversion risks.</p>
684
- <div className="meter" aria-label="Round-trip confidence"><span style={{ width: `${confidence}%` }} /></div>
703
+ <aside className={`side-panel right-panel${rightPanelCollapsed ? " collapsed" : ""}`}>
704
+ <div className="panel-chrome">
705
+ <h2 className="panel-title">Output Health</h2>
706
+ <button
707
+ type="button"
708
+ className="panel-toggle"
709
+ aria-label={rightPanelCollapsed ? "Expand Output Health panel" : "Collapse Output Health panel"}
710
+ aria-expanded={!rightPanelCollapsed}
711
+ onClick={() => setRightPanelCollapsed((collapsed) => !collapsed)}
712
+ title={rightPanelCollapsed ? "Expand Output Health" : "Collapse Output Health"}
713
+ >
714
+ <span aria-hidden="true">≡</span>
715
+ </button>
685
716
  </div>
717
+ <div className="panel-body">
718
+ <div className="source-card">
719
+ <strong>Round-trip confidence</strong>
720
+ <p>Tables, images, links, and headings are tracked as conversion risks.</p>
721
+ <div className="meter" aria-label="Round-trip confidence"><span style={{ width: `${confidence}%` }} /></div>
722
+ </div>
686
723
 
687
- <h2 className="panel-title">Table Tools</h2>
688
- <div className="tool-group">
689
- <button disabled={!canUseRich || !editor?.isActive("table")} onClick={() => editor?.chain().focus().addRowAfter().run()}>Row +</button>
690
- <button disabled={!canUseRich || !editor?.isActive("table")} onClick={() => editor?.chain().focus().addColumnAfter().run()}>Col +</button>
691
- <button disabled={!canUseRich || !editor?.isActive("table")} onClick={() => editor?.chain().focus().deleteRow().run()}>Row -</button>
692
- <button disabled={!canUseRich || !editor?.isActive("table")} onClick={() => editor?.chain().focus().deleteColumn().run()}>Col -</button>
693
- <button disabled={!canUseRich || !editor?.isActive("table")} onClick={() => editor?.chain().focus().toggleHeaderRow().run()}>Header</button>
694
- <button disabled={!canUseRich || !editor?.isActive("table")} onClick={() => editor?.chain().focus().deleteTable().run()}>Delete</button>
695
- </div>
724
+ <h2 className="panel-title">Table Tools</h2>
725
+ <div className="tool-group">
726
+ <button disabled={!canUseRich || !editor?.isActive("table")} onClick={() => editor?.chain().focus().addRowAfter().run()}>Row +</button>
727
+ <button disabled={!canUseRich || !editor?.isActive("table")} onClick={() => editor?.chain().focus().addColumnAfter().run()}>Col +</button>
728
+ <button disabled={!canUseRich || !editor?.isActive("table")} onClick={() => editor?.chain().focus().deleteRow().run()}>Row -</button>
729
+ <button disabled={!canUseRich || !editor?.isActive("table")} onClick={() => editor?.chain().focus().deleteColumn().run()}>Col -</button>
730
+ <button disabled={!canUseRich || !editor?.isActive("table")} onClick={() => editor?.chain().focus().toggleHeaderRow().run()}>Header</button>
731
+ <button disabled={!canUseRich || !editor?.isActive("table")} onClick={() => editor?.chain().focus().deleteTable().run()}>Delete</button>
732
+ </div>
696
733
 
697
- <h2 className="panel-title">Image Tools</h2>
698
- <div className="tool-group">
699
- <button disabled={!canUseRich} onClick={insertImageFile}>File</button>
700
- <button disabled={!canUseRich} onClick={insertImageUrl}>URL</button>
701
- </div>
734
+ <h2 className="panel-title">Image Tools</h2>
735
+ <div className="tool-group">
736
+ <button disabled={!canUseRich} onClick={insertImageFile}>File</button>
737
+ <button disabled={!canUseRich} onClick={insertImageUrl}>URL</button>
738
+ </div>
702
739
 
703
- <div className="checklist">
704
- <div className="check"><span className="dot" /><div><b>Markdown target</b><br />{markdownPath || "Choose Save MD As..."}</div></div>
705
- <div className="check"><span className="dot" /><div><b>Word export</b><br />{exportPath || "Ready to export with template hook."}</div></div>
706
- <div className="check"><span className={imageCount ? "dot warn" : "dot"} /><div><b>Images</b><br />{imageCount} detected. Asset folder policy still needs final pass.</div></div>
707
- <div className="check"><span className={tableCount ? "dot warn" : "dot"} /><div><b>Tables</b><br />{tableCount} detected. Advanced cell controls are surfaced here.</div></div>
740
+ <div className="checklist">
741
+ <div className="check"><span className="dot" /><div><b>Markdown target</b><br />{markdownPath || "Choose Save MD As..."}</div></div>
742
+ <div className="check"><span className="dot" /><div><b>Word export</b><br />{exportPath || "Ready to export with template hook."}</div></div>
743
+ <div className="check"><span className={imageCount ? "dot warn" : "dot"} /><div><b>Images</b><br />{imageCount} detected. Asset folder policy still needs final pass.</div></div>
744
+ <div className="check"><span className={tableCount ? "dot warn" : "dot"} /><div><b>Tables</b><br />{tableCount} detected. Advanced cell controls are surfaced here.</div></div>
745
+ </div>
708
746
  </div>
709
747
  </aside>
710
748
 
@@ -127,6 +127,18 @@ main.app-shell {
127
127
  var(--paper);
128
128
  }
129
129
 
130
+ main.app-shell.left-collapsed {
131
+ grid-template-columns: 58px 44px minmax(560px, 1fr) 320px;
132
+ }
133
+
134
+ main.app-shell.right-collapsed {
135
+ grid-template-columns: 58px 260px minmax(560px, 1fr) 44px;
136
+ }
137
+
138
+ main.app-shell.left-collapsed.right-collapsed {
139
+ grid-template-columns: 58px 44px minmax(560px, 1fr) 44px;
140
+ }
141
+
130
142
  .brand-rail {
131
143
  grid-row: 1 / 4;
132
144
  border-right: 1px solid var(--line);
@@ -208,6 +220,11 @@ main.app-shell {
208
220
  overflow: auto;
209
221
  }
210
222
 
223
+ .side-panel.collapsed {
224
+ overflow: hidden;
225
+ padding: 8px 5px;
226
+ }
227
+
211
228
  .left-panel {
212
229
  grid-row: 2 / 3;
213
230
  grid-column: 2 / 3;
@@ -220,6 +237,53 @@ main.app-shell {
220
237
  border-left: 1px solid var(--line);
221
238
  }
222
239
 
240
+ .panel-chrome {
241
+ min-height: 28px;
242
+ display: flex;
243
+ align-items: center;
244
+ justify-content: space-between;
245
+ gap: 8px;
246
+ margin-bottom: 10px;
247
+ }
248
+
249
+ .left-panel .panel-chrome {
250
+ flex-direction: row-reverse;
251
+ }
252
+
253
+ .panel-toggle {
254
+ width: 28px;
255
+ min-width: 28px;
256
+ height: 28px;
257
+ min-height: 28px;
258
+ display: grid;
259
+ place-items: center;
260
+ padding: 0;
261
+ font-size: 17px;
262
+ line-height: 1;
263
+ }
264
+
265
+ .side-panel.collapsed .panel-chrome {
266
+ height: 100%;
267
+ min-height: 0;
268
+ flex-direction: column;
269
+ justify-content: flex-start;
270
+ margin: 0;
271
+ }
272
+
273
+ .side-panel.collapsed .panel-title {
274
+ writing-mode: vertical-rl;
275
+ transform: rotate(180deg);
276
+ white-space: nowrap;
277
+ margin: 10px 0 0;
278
+ max-height: calc(100vh - 150px);
279
+ overflow: hidden;
280
+ text-overflow: ellipsis;
281
+ }
282
+
283
+ .side-panel.collapsed .panel-body {
284
+ display: none;
285
+ }
286
+
223
287
  .panel-title {
224
288
  margin: 0 0 10px;
225
289
  color: var(--muted);
@@ -685,7 +749,7 @@ main.app-shell {
685
749
  white-space: nowrap;
686
750
  }
687
751
 
688
- @media (max-width: 1120px) {
752
+ @media (max-width: 840px) {
689
753
  body {
690
754
  min-width: 760px;
691
755
  }
@@ -1,13 +1,13 @@
1
- import { defineConfig } from "vite";
2
- import react from "@vitejs/plugin-react";
3
- import path from "node:path";
4
-
5
- export default defineConfig({
6
- root: path.resolve(import.meta.dirname),
7
- base: "./",
8
- plugins: [react()],
9
- build: {
10
- outDir: "../desktop/BuildCorpusEditor/wwwroot",
11
- emptyOutDir: true,
12
- },
13
- });
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import path from "node:path";
4
+
5
+ export default defineConfig({
6
+ root: path.resolve(import.meta.dirname),
7
+ base: "./",
8
+ plugins: [react()],
9
+ build: {
10
+ outDir: "../desktop/BuildCorpusEditor/wwwroot",
11
+ emptyOutDir: true,
12
+ },
13
+ });
@@ -1,21 +1,21 @@
1
- {
2
- "conversion": {
3
- "equations": "tex",
4
- "images": "assets"
5
- },
6
- "output": {
7
- "out": ".codex/build-corpus/out",
8
- "out_same_dir": false
9
- },
10
- "s3": {
11
- "bucket": "build-corpus-assets",
12
- "public_base_url": "https://assets.example.com",
13
- "prefix": "build-corpus",
14
- "endpoint_url": "https://ACCOUNT_ID.r2.cloudflarestorage.com",
15
- "region_name": "auto",
16
- "access_key_id": "%R2_ACCESS_KEY_ID%",
17
- "secret_access_key": "%R2_SECRET_ACCESS_KEY%",
18
- "cache_control": "public, max-age=31536000, immutable",
19
- "acl": null
20
- }
21
- }
1
+ {
2
+ "conversion": {
3
+ "equations": "tex",
4
+ "images": "assets"
5
+ },
6
+ "output": {
7
+ "out": ".codex/build-corpus/out",
8
+ "out_same_dir": false
9
+ },
10
+ "s3": {
11
+ "bucket": "build-corpus-assets",
12
+ "public_base_url": "https://assets.example.com",
13
+ "prefix": "build-corpus",
14
+ "endpoint_url": "https://ACCOUNT_ID.r2.cloudflarestorage.com",
15
+ "region_name": "auto",
16
+ "access_key_id": "%R2_ACCESS_KEY_ID%",
17
+ "secret_access_key": "%R2_SECRET_ACCESS_KEY%",
18
+ "cache_control": "public, max-age=31536000, immutable",
19
+ "acl": null
20
+ }
21
+ }