This commit is contained in:
2026-09-12 18:16:27 +08:00
parent b7f55aa631
commit 397a7a8cc4
96 changed files with 1167 additions and 1259 deletions
@@ -98,7 +98,7 @@ translations are held to under <a class="xref" href="../conventions/translation-
<h2 id="the-three-bundles">The Three Bundles</h2>
<p>Each locale is a folder under the i18n directory holding one file per namespace plus an index file
that re-exports them as a single object. The three folders carry identical file lists — the same
twenty-three namespaces, from <code>apiErrors</code> and <code>auth</code> through <code>tree</code> and <code>widgets</code> — because a
twenty-two namespaces, from <code>apiErrors</code> and <code>auth</code> through <code>tree</code> and <code>widgets</code> — because a
namespace's keys are added to all three locales in the same change. Keys are named
<code>namespace.area.element</code> in lowerCamelCase and are split by the UI region they serve rather than by
the source file that reads them.</p>
@@ -332,7 +332,7 @@ and the re-title registration.</li>
<li><code>wwwroot-src/src/i18n/schema.ts</code> — the English bundle as <code>MessageSchema</code>.</li>
<li><code>wwwroot-src/src/i18n/schema.d.ts</code> — the ambient augmentation that registers that shape as
vue-i18n's global message schema.</li>
<li><code>wwwroot-src/src/i18n/en/index.ts</code> — the English bundle: the twenty-three namespaces assembled into
<li><code>wwwroot-src/src/i18n/en/index.ts</code> — the English bundle: the twenty-two namespaces assembled into
one object.</li>
<li><code>wwwroot-src/src/i18n/zh-Hant/index.ts</code> and <code>wwwroot-src/src/i18n/zh-Hans/index.ts</code> — the same
assembly, each annotated with <code>MessageSchema</code>.</li>
@@ -212,7 +212,7 @@ not per browser: two browsers pointed at the same service read and write the sam
machining project is server-side in the same way — one project is loaded at a time and every connected
browser sees it, which is why the status-hub broadcast is what keeps a second tab honest.</p>
<p>The URL carries the rest: the route, the <code>?tree=</code> selection on the two Control-Tree pages, the browsed
path on the File Explorer route, and the tab segments on the Tool House and Controller routes. Those
path on the File Explorer route, and the tab segments on the Tool House route. Those
come back on a reload from the address bar, and when a Control-Tree page is opened with no <code>?tree=</code> the
host lands on the stored last selection for that page, falling back to the page's root when the stored
id is not in the current project's tree.</p>
@@ -286,13 +286,13 @@ focus starts a fresh burst immediately.</p>
Because pages are cached, navigating away does not close one, and whether the backend engine stops
drawing for it depends on the host. The Execution, General Setup and Tool House pages hand their
activation flag to the canvas's <code>active</code> prop, which pauses the engine while the page sits off-screen;
the Controller, Machine Tool and Mech Builder canvases and the File Explorer's STL preview bind no
the Machine Tool and Mech Builder canvases and the File Explorer's STL preview bind no
such prop, and the Execution page's CWE canvas pins it true, so all of those keep rendering until
something unmounts them. Collapsing a panel does close one, unless that panel keeps its content
mounted: the Execution page's 3D canvas panel does, so it survives a collapse, while the CWE panel
beside it does not and its canvas connection goes with it.</li>
<li>The cleanup hub is opened once per Control-Tree host and once each on the Controller and Tool House
pages. Its composable also holds the set of IndexService keys that owner registered; on unmount it
<li>The cleanup hub is opened once per Control-Tree host and once on the Tool House page. Its composable
also holds the set of IndexService keys that owner registered; on unmount it
posts a removal for every one of them and then stops the connection, so a teardown releases the
server-side objects the page had indexed even if the hub never connected.</li>
</ul>
@@ -364,9 +364,8 @@ guard on the <code>?tree=</code> sync, the landing selection, and the cleanup re
and the panel that keeps the 3D canvas mounted through a collapse.</li>
<li><code>wwwroot-src/src/pages/GeneralSetupPage.vue</code> — the same lifecycle for the equipment host.</li>
<li><code>wwwroot-src/src/pages/ToolHousePage.vue</code> — the page that reloads its singleton explicitly on mount.</li>
<li><code>wwwroot-src/src/pages/ControllerPage.vue</code>, <code>wwwroot-src/src/pages/MachineToolPage.vue</code>,
<code>wwwroot-src/src/pages/MechBuilderPage.vue</code> and <code>wwwroot-src/src/components/StlPreviewPane.vue</code> —
the canvases mounted with no <code>active</code> binding.</li>
<li><code>wwwroot-src/src/pages/MachineToolPage.vue</code>, <code>wwwroot-src/src/pages/MechBuilderPage.vue</code> and
<code>wwwroot-src/src/components/StlPreviewPane.vue</code> — the canvases mounted with no <code>active</code> binding.</li>
<li><code>wwwroot-src/src/components/execution/StepVolumePanel.vue</code> — the CWE canvas that pins <code>active</code> true
and is stopped only by its own unmount.</li>
<li><code>wwwroot-src/src/pages/FileExplorerPage.vue</code> — the route-leave guard that settles the editor buffer.</li>
@@ -133,11 +133,6 @@ is one of its children, so each renders inside the Main Panel's frame and menu b
<td>The machining chain on a canvas of its own, beside a left column that shows either a read-only identity panel or the chain's XML source — two states of one column, chosen with a GUI/XML toggle. A load button installs a chain file into the project. Carries no Page-menu entry and is reached by URL.</td>
</tr>
<tr>
<td><code>/controller/:tab?</code></td>
<td><code>controller</code></td>
<td>The superseded HardNcEnv controller surface. The optional segment mirrors the active tab.</td>
</tr>
<tr>
<td><code>/tool-house/:toolId(\d+)?/:tab?/:subtab?</code></td>
<td><code>tool-house</code></td>
<td>The tool library and the per-tool editor. The three optional segments carry the selected tool and the two nested tab levels.</td>
@@ -170,9 +165,9 @@ is one of its children, so each renders inside the Main Panel's frame and menu b
</tbody>
</table>
<p>The menu bar's Page dropdown holds every one of these that is menu-reachable, in setup order: Tool
House, General Setup and Execution, then File Explorer and Mechanism Builder below a separator,
then the legacy Controller below a second one. The Log Viewer has a button of its own rather than a
menu entry, and <code>/machine-tool</code> has neither — it is reached by URL.</p>
House, General Setup and Execution, then File Explorer and Mechanism Builder below a separator. The
Log Viewer has a button of its own rather than a menu entry, and <code>/machine-tool</code> has neither — it is
reached by URL.</p>
<p>The Tool House route's tool segment is constrained to digits, but all three of its segments are
optional, so a non-numeric segment in the tool position is not rejected: the router skips the tool
parameter and reads that segment as the tab, which is what makes <code>/tool-house/cutter</code> a working tab
@@ -193,6 +188,10 @@ moved into a Control Tree still lands on the editor.</p>
</thead>
<tbody>
<tr>
<td><code>/controller/:rest(.*)*</code></td>
<td><code>/general-setup?tree=equipment/controller</code> — any trailing path is discarded, the retired Controller page's <code>/&lt;tab&gt;</code> segment included, so every form lands on the branch root.</td>
</tr>
<tr>
<td><code>/spindle-capability/:tab?</code></td>
<td><code>/general-setup?tree=equipment/spindle</code>, with <code>/&lt;tab&gt;</code> appended when the segment is one of <code>thermal</code>, <code>gear-shift</code>, <code>dry-run</code>, <code>power</code>, <code>torque</code>. An unrecognised segment is dropped and the branch root is selected.</td>
</tr>
@@ -321,8 +320,9 @@ expansion set still unfolds the branches it names;</li>
<li>as the first step of <code>routeForTreeId</code>, so the id-to-page resolver never sees an unmigrated id.</li>
</ul>
<p>Note what the first rule implies: a bare <code>?tree=controller</code> selects the <code>equipment/controller</code>
branch — the current controller editor on the General Setup page — and has nothing to do with the
<code>/controller</code> route, which is the separate legacy HardNcEnv surface.</p>
branch — the controller editor on the General Setup page — and the redirect-only <code>/controller</code> path
above lands on the same branch, so a bookmark from the retired Controller page's era resolves in
either form.</p>
<h2 id="landing-an-id-on-its-page">Landing an Id on Its Page</h2>
<p><code>TREE_PAGE_ROOTS</code> lists the first segments that name a page: <code>execution</code>, <code>equipment</code> and
<code>toolhouse</code>. <code>spindle</code> is absent on purpose — migration folds it into <code>equipment/…</code> before any root
@@ -356,18 +356,18 @@ it, whereas the tool step is tested on its own, so a second segment that is not
can carry is discarded either way — an id pointing at a single flute contour under
<code>…/cutter/contours/fluting/…</code> lands on the nearest tab the URL can express. The same three name
lists are imported by the page itself, so the tab set and the translation cannot drift apart.</p>
<p>Once on the page, the tab segments are kept in step with the tabs by <code>useRouteTabs</code>, the composable
the Controller route uses as well. It gives each tab level one route param, a valid-name set and a
<p>Once on the page, the tab segments are kept in step with the tabs by <code>useRouteTabs</code>, a composable
whose only consumer this page is. It gives each tab level one route param, a valid-name set and a
default; a nested level may declare which parent values make it apply, and while its parent is
something else the level is <strong>dormant</strong> — its segment is dropped from the URL while its reference
quietly remembers the last value, so returning to that parent tab returns to the sub-tab it had.
The Tool House sub-tab level applies under <code>cutter</code> and <code>holder</code> only, and both its valid set and
its default depend on which of the two is showing. A segment the level does not recognise resolves
to that default rather than failing. Canonicalisation runs on the first mount and on every
keep-alive re-entry, which is why a bare <code>/controller</code> becomes <code>/controller/coordinate-table</code> and a
bare <code>/tool-house</code> acquires its tab segments. The tool segment is not one of the composable's
levels: the page fills it in itself, adopting the URL's tool when the project holds it and the first
tool in the list otherwise, then replacing the URL with what it adopted. That resolver reacts to the
keep-alive re-entry, which is why a bare <code>/tool-house</code> acquires its tab segments. The tool segment
is not one of the composable's levels: the page fills it in itself, adopting the URL's tool when the
project holds it and the first tool in the list otherwise, then replacing the URL with what it
adopted. That resolver reacts to the
<code>toolId</code> param and to the tool list rather than to page activation, so it also re-points the URL
when the list changes underneath it.</p>
<p>The Tool House page never reads or writes <code>?tree=</code>. A <code>toolhouse/…</code> id reaches it only by way of
@@ -452,8 +452,8 @@ and the retitle hook registered with the i18n module.</li>
<code>rawTreePathOfRoute</code>, <code>treePathOfRoute</code>, the two selection/URL watchers and the unguarded
controller-checkbox watcher beside them, <code>landingSelection</code>, <code>applyRouteSelection</code> and
<code>syncUrlBack</code>, plus the build-time reveal of a URL-named CSV or CL controller node.</li>
<li><code>wwwroot-src/src/composables/useRouteTabs.ts</code> — the tab-to-param sync used by the Tool House and
Controller routes, including dormant levels and the keep-alive re-entry canonicalisation.</li>
<li><code>wwwroot-src/src/composables/useRouteTabs.ts</code> — the tab-to-param sync the Tool House route uses,
including dormant levels and the keep-alive re-entry canonicalisation.</li>
<li><code>wwwroot-src/src/pages/ToolHousePage.vue</code> — consumes the shared tab lists, resolves the tool from
its path param, and navigates by route name when the tool changes.</li>
<li><code>wwwroot-src/src/pages/FileExplorerPage.vue</code> — mirrors the browsed location into the route's