This commit is contained in:
2026-09-09 17:35:23 +08:00
parent 2a98ae7556
commit 161af68fe3
1271 changed files with 58251 additions and 98639 deletions
@@ -0,0 +1,152 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Anatomy by Source Directory | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Anatomy by Source Directory | HiAPI-C# 2025 ">
<link rel="icon" href="../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../public/docfx.min.css">
<link rel="stylesheet" href="../../public/main.css">
<meta name="docfx:navrel" content="../../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../index.html">
<img id="logo" class="svg" src="../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-BySource">
<h1 id="anatomy-by-source-directory">Anatomy by Source Directory</h1>
<p>The rest of Anatomy is keyed on the shipped surface — a route, a Control-Tree branch, a reusable
control. This layer is keyed on the source tree instead, and it is the entry point for the reader who
knows which directory a change landed in but not which screen it shows up on.</p>
<p>Ordered the way a request travels: the front end first, then the process that serves it.</p>
<h2 id="directories">Directories</h2>
<ul>
<li><a href="webservice-spa/index.html">Web Service SPA Source Tree</a> — the Quasar front end under
<code>wwwroot-src/src</code>: how <code>components/</code> is grouped two ways at once, what the Control-Tree registry
actually is, and why half the route table is redirects</li>
<li><a href="webservice-backend/index.html">Web Service Backend Source Tree</a> — the ASP.NET Core half: how to read
a filename, why folder nesting does not predict a route, and which declared hub is never mapped</li>
</ul>
<h2 id="what-this-layer-is-for">What This Layer Is For</h2>
<p>A page in the rest of Anatomy answers <em>what is this thing on my screen made of</em>. Both of these answer
the inverse: <em>this directory changed — what does it show up as, and which page is now wrong</em>. That
makes them the first stop after a refactor, and the reason each directory entry names the pages that
document it.</p>
<p>They are directory indexes, not file listings. A file worth naming is named on the page that
documents the surface it implements; what lives here is the shape of the tree, the conventions that
hold across it, and the traps that survive a careful reading of any single file.</p>
<h2 id="what-is-not-indexed-here">What Is Not Indexed Here</h2>
<p>The HiAPI engine repositories are <strong>not</strong> given a directory document. Anatomy cites engine source
where a shipped surface depends on it, but the engine's own structure is covered by the generated
<a href="../../api/Hi.Machining.html">API Reference</a>, which stays correct in a way a hand-written index could
not.</p>
<p>Neither is the outgoing Windows desktop client. It takes no new feature work, so a hand-written
index of its tree drifts with every flagship change and is read by nobody; the same reasoning
retired the page-by-page map of its files.</p>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="../index.html">HiNC App Anatomy</a> — the section index, and the surface-keyed way in</li>
<li><a class="xref" href="webservice-spa/index.html">Web Service SPA Source Tree</a> — the flagship front end</li>
<li><a class="xref" href="webservice-backend/index.html">Web Service Backend Source Tree</a> — the process that
serves it</li>
<li><a class="xref" href="../platform/index.html">Platform</a> — the machinery under every screen, entered by mechanism rather
than by directory</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>
@@ -0,0 +1,28 @@
<div id="sidetoggle">
<div>
<div class="sidefilter">
<form class="toc-filter">
<span class="glyphicon glyphicon-filter filter-icon"></span>
<span class="glyphicon glyphicon-remove clear-icon" id="toc_filter_clear"></span>
<input type="text" id="toc_filter_input" placeholder="Filter by title" onkeypress="if(event.keyCode==13) {return false;}">
</form>
</div>
<div class="sidetoc">
<div class="toc" id="toc">
<ul class="nav level1">
<li>
<a href="index.html" name="" title="Overview">Overview</a>
</li>
<li>
<a href="webservice-spa/index.html" name="" title="Web Service SPA">Web Service SPA</a>
</li>
<li>
<a href="webservice-backend/index.html" name="" title="Web Service Backend">Web Service Backend</a>
</li>
</ul>
</div>
</div>
</div>
</div>
@@ -0,0 +1,2 @@
{"items":[{"name":"Overview","href":"index.html","topicHref":"index.html"},{"name":"Web Service SPA","href":"webservice-spa/index.html","topicHref":"webservice-spa/index.html"},{"name":"Web Service Backend","href":"webservice-backend/index.html","topicHref":"webservice-backend/index.html"}]}
@@ -0,0 +1,300 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Web Service Backend Source Tree | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Web Service Backend Source Tree | HiAPI-C# 2025 ">
<link rel="icon" href="../../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../../public/docfx.min.css">
<link rel="stylesheet" href="../../../public/main.css">
<meta name="docfx:navrel" content="../../../toc.html">
<meta name="docfx:tocrel" content="../toc.html">
<meta name="docfx:rel" content="../../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../../index.html">
<img id="logo" class="svg" src="../../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-BySource-WebserviceBackend">
<h1 id="web-service-backend-source-tree">Web Service Backend Source Tree</h1>
<p>The C# half of <code>HiNC-2025-webservice</code> is an ASP.NET Core process that serves the SPA, answers its
REST calls, and pushes to it over SignalR. It is organised by the <strong>engine's domain vocabulary</strong>, not
by web-framework artifact type: a controller, its hub, its DI service and its non-web scene objects
sit together in one domain folder rather than in a <code>Controllers</code> / <code>Hubs</code> / <code>Services</code> split.</p>
<p>Folders below are ordered by how much of the shipped surface they answer for.</p>
<h2 id="reading-a-filename">Reading a Filename</h2>
<p>The role of a file is told by its suffix and base type, and the convention holds throughout:</p>
<table>
<thead>
<tr>
<th>Suffix</th>
<th>What it is</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>*Controller.cs</code></td>
<td>a REST controller with a route attribute</td>
</tr>
<tr>
<td><code>*Hub.cs</code></td>
<td>a SignalR hub — but see the two traps below</td>
</tr>
<tr>
<td><code>*Service.cs</code></td>
<td>a singleton registered in <code>Program.cs</code></td>
</tr>
<tr>
<td><code>*Displayee.cs</code></td>
<td>a scene-composition object handed to the engine; no HTTP surface at all</td>
</tr>
<tr>
<td><code>*Dtos.cs</code>, <code>*Config.cs</code></td>
<td>plain wire or settings types</td>
</tr>
</tbody>
</table>
<p>Two names deliberately break the reader's expectation. <code>Controller/ControllerController.cs</code> means
<strong>CNC controller</strong>, not an MVC controller. And <code>Mech/</code>'s <code>*DisplayController.cs</code> files are ordinary
REST controllers that attach a displayee to a rendering connection somebody else already owns — they
are not hubs.</p>
<div class="IMPORTANT">
<h5>Important</h5>
<p><strong>Folder nesting does not predict the route.</strong> <code>Mech/Topo/</code> and <code>Mech/MechBuilder/</code> expose routes
that carry no <code>api/mech/</code> prefix at all, and even inside <code>Mech/</code> two conventions coexist: the newer
ports use kebab-case paths under <code>api/mech/</code>, while the older keyed-object editors use the
controller-name default. Read the route attribute, never the path.</p>
</div>
<h2 id="composition-root">Composition Root</h2>
<p><code>Program.cs</code> is the only C# file at the repository root, and it is where the questions a reader
usually has are actually answered: which services are singletons, which hubs are mapped, and what the
middleware order is. Two facts about it are worth carrying:</p>
<ul>
<li><strong>A declared hub is not a mapped hub.</strong> <code>Program.cs</code> maps eight hub endpoints. Anything not in that
list is unreachable however complete its class looks — see the trap under <code>Execution/</code> below.</li>
<li><strong>The tree is not self-contained.</strong> The project service types <code>Program.cs</code> leans on hardest live in
the sibling HiNc engine repository, not here.</li>
</ul>
<p>Documented in <a class="xref" href="../../platform/program-and-hosting.html">Program and Hosting</a>.</p>
<h2 id="execution">Execution</h2>
<p><code>Execution/</code> is the run cockpit's whole backend: playback control, the strip and cycle-line charts,
the NC-program branch index, run-output queries, and the real-time push layer.</p>
<ul>
<li><code>Execution/ExecutionController.cs</code> — the playback surface.</li>
<li><code>Execution/ExecutionChartsController.cs</code> and <code>Execution/ClStripController.cs</code> — the chart data.
Three separate classes share the case-insensitive <code>api/execution</code> prefix on purpose, one of them
routed there rather than under Mission with a comment saying why.</li>
<li><code>Execution/SessionSinkHub.cs</code> — <strong>four</strong> mapped hubs declared in one file, one per message sink, so
hub-per-file does not hold here. Each pairs with a broadcast service that <code>Program.cs</code> resolves
eagerly, so it subscribes to its sink before the first client connects.</li>
<li><code>Execution/ClStripHub.cs</code>, <code>Execution/ExecutionStatusHub.cs</code> — the strip and status pushes.</li>
</ul>
<div class="WARNING">
<h5>Warning</h5>
<p><code>Execution/ExecutionCanvasHub.cs</code> declares a hub that <code>Program.cs</code> never maps and that nothing in
the repository references. The Execution canvas rides <code>Disp/RenderingHub.cs</code> like every other
canvas. A reader looking for &ldquo;the hub behind the Execution page&rdquo; by name will find this file first
and be wrong.</p>
</div>
<p>Documented in <a class="xref" href="../../execution/index.html">Execution Page</a>,
<a class="xref" href="../../execution/program.html">Program Branch</a>,
<a class="xref" href="../../execution/selected-step-info-panel.html">Selected-Step Info Panel</a>,
<a class="xref" href="../../execution/strip-charts.html">Strip Charts</a> and <a class="xref" href="../../execution/cycle-line-charts.html">Cycle-Line Charts</a>.</p>
<h2 id="mech">Mech</h2>
<p><code>Mech/</code> is the largest domain folder: the REST surfaces for the machine tool, the tool house and
cutter editing, fixtures and workpieces, spindle capability, background and coolant, and the three
runner suits. It also holds the display controllers that bind a mechanism scene onto a rendering
connection. <code>Mech/CutterDtoBuilder.cs</code> is the shared read-side DTO shape two controllers reuse, and
<code>Mech/NcSuitUsage.cs</code> is what lets the Control Tree show the CSV and CL runner branches only when the
loaded project actually plays them. No SignalR hub lives here.</p>
<ul>
<li><code>Mech/MechBuilder/</code> — the standalone mechanism-building session, held by a process-wide singleton
rather than per user. Documented in <a class="xref" href="../../util/mech-builder.html">Mechanism Builder Page</a>.</li>
<li><code>Mech/Topo/</code> — one thin controller per transformer kind, each editing an instance held in the
keyed object store. Documented in
<a class="xref" href="../../geom/transformer-panel.html">Transformer Select Panel</a>.</li>
<li><code>Mech/SoftNcRunnerController.cs</code> — the one endpoint family behind the Controller branch, and the
file whose snapshot decides which of that branch's nodes exist. Documented in
<a class="xref" href="../../general-setup/controller/index.html">Controller Branch</a>, with
<a class="xref" href="../../general-setup/controller/brand-matrix.html">Brand Matrix</a> for the snapshot flags themselves.</li>
<li><code>Mech/CsvRunnerController.cs</code> and <code>Mech/ClRunnerController.cs</code> — the two resident non-brand runner
suits, reached from tree branches the Preference menu hides by default. Documented in
<a class="xref" href="../../shell/preference-menu.html">Preference Menu Dropdown</a> until those branches have a page.</li>
</ul>
<p>Documented in <a class="xref" href="../../general-setup/index.html">General Setup Page</a> and
<a class="xref" href="../../tool-house/index.html">Tool House Page</a> and the panels beneath them.</p>
<h2 id="missions">Missions</h2>
<p><code>Missions/</code> is the mission command tree's backend. Two things here surprise readers:</p>
<ul>
<li>The <strong>folder is plural and the route is singular</strong> — and the SPA folder is singular too.</li>
<li><code>Missions/NcOptOptionEndpoints.cs</code> is not a minimal-API endpoint file despite the name. It is a
second file of the same partial controller class, which is why its routes resolve under the mission
prefix.</li>
</ul>
<p><code>Missions/MissionCommandCatalog.cs</code> reflects over every session command carrying the catalog
attribute once per process, replacing hand-maintained kind switches, and
<code>Missions/MissionCommandFields.cs</code> does the same for annotated scalars — which is how a simple
command gets an editor without a bespoke panel. <code>Missions/ScriptCompletionService.cs</code> and
<code>Missions/ScriptCompileCheckService.cs</code> are the Roslyn pair behind the script editor; the compile
check reuses the same options and globals type the script command uses at run time.</p>
<p>Documented in <a class="xref" href="../../execution/mission/mission-root-panel.html">Mission Root Panel</a> and the command panels under it.</p>
<h2 id="disp">Disp</h2>
<p><code>Disp/</code> is the rendering layer. <code>Disp/RenderingHub.cs</code> is the single transport surface for every 3D
canvas in the application — canvas initialization, pointer, key and touch input, resize, view
presets, cache clearing and snapshots — and the SPA's canvas component defaults to it, so all pages
share one hub rather than one hub per page. <code>Disp/RenderingService.cs</code> owns the per-connection
engines the hub resolves against. <code>Disp/StlPreviewController.cs</code> is the only other REST surface here.</p>
<p>The <code>*Displayee.cs</code> files are scene-graph composition objects rather than web types, and each has a
live instantiation site: the execution scene, the equipment-setup scene, the step-subtraction scene
and the two coordinate-frame displayees the first two compose in.</p>
<p>Documented in <a class="xref" href="../../conventions/rendering-canvas-web-service.html">Rendering Canvas on Web Service</a>,
<a class="xref" href="../../widget/renderingcanvas-tool-bar.html">RenderingCanvas Tool Bar</a> and — for
<code>Disp/StlPreviewController.cs</code> and the per-connection slot behind it —
<a class="xref" href="../../util/stl-preview-pane.html">STL Preview Pane</a>.</p>
<h2 id="environments">Environments</h2>
<p><code>Environments/</code> is session and environment scope: the project lifecycle, per-user preference
persistence, the Execution page's division flags, and the shipped localized step-present catalog. It
is also where the Log Viewer's data comes from — the log endpoints sit on the project controller
beside status, new, load, save, reload, save-as and close.</p>
<p>Documented in <a class="xref" href="../../shell/main-panel.html">Main Panel</a>,
<a class="xref" href="../../platform/session-state.html">Session State</a>,
<a class="xref" href="../../shell/preference-menu.html">Preference Menu Dropdown</a>,
<a class="xref" href="../../platform/i18n.html">Internationalization</a>,
<a class="xref" href="../../platform/log-viewer.html">Log Viewer Page</a> and
<a class="xref" href="../../execution/step-present-dialog.html">Step Present Dialog</a>.</p>
<h2 id="common-geom-and-widget">Common, Geom and Widget</h2>
<ul>
<li><code>Common/</code> — cross-cutting infrastructure with no single domain owner: the optional login gate, the
named-root file explorer, path guards, the daily file logger, and two pieces the whole application
rests on. <code>Common/IndexService.cs</code> is the keyed object store behind the <em>index a backend object,
then edit it by key</em> pattern every geometry, transformer and widget controller uses.
<code>Common/CleanupHub.cs</code> is not a messaging hub, and it is not what bounds that store either. Its
key registry is an ordinary instance property and SignalR builds a fresh hub instance for every
invocation, so the entry <code>Add</code> records is discarded with the instance that received it and the
disconnect handler always walks an empty registry. The bound comes from the browser instead:
<code>wwwroot-src/src/composables/useCleanupHub.ts</code> posts the index-remove endpoint on
<code>Common/IndexController.cs</code> for every key it holds when its host unmounts, and again whenever a
key it registered is replaced. Documented in
<a class="xref" href="../../conventions/dictionary-service-pattern.html">Dictionary Service Pattern</a>,
<a class="xref" href="../../conventions/webapi-hub-cleanup-pattern.html">WebAPI Hub Cleanup Pattern</a>,
<a class="xref" href="../../platform/login-and-auth.html">Login and Authentication</a> and
<a class="xref" href="../../platform/log-viewer.html">Log Viewer Page</a>.</li>
<li><code>Geom/</code> — one CRUD controller per geometry kind, on the same keyed-object pattern, and nothing
else: no hubs, no services. Documented in <a class="xref" href="../../geom/index.html">Geometry Panels</a>.</li>
<li><code>Widget/</code> — the generic value and lifecycle controllers the reusable inputs post to: the 3D vector,
the 4×4 matrix, and the object-management surface that handles file operations, XML editing and
copy-paste for indexed objects. Documented in
<a class="xref" href="../../widget/object-management-menu-button.html">Object Management Menu Button</a>,
<a class="xref" href="../../widget/vec3d-control.html">Vec3dControl Component</a> and
<a class="xref" href="../../widget/mat4d-control.html">Mat4dControl Component</a>.</li>
</ul>
<h2 id="controller-and-demo">Controller and Demo</h2>
<p><code>Controller/</code> is the single-file surface behind the legacy controller page. It has a successor in
<code>Mech/SoftNcRunnerController.cs</code>, whose own doc comment says so, but <strong>both are live</strong> — the legacy
page has not been removed. Documented in <a class="xref" href="../../legacy-controller.html">Legacy Controller</a>.</p>
<p><code>Demo/</code> is customer-facing sample code — a custom colour guide and a per-step optimization sweep —
compiled into the assembly with no call site in the running application. Read it as an example of
how to extend the engine, not as part of the shipped behaviour.</p>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="../index.html">Anatomy by Source Directory</a> — the other two source trees, and how this
layer is meant to be entered</li>
<li><a class="xref" href="../webservice-spa/index.html">Web Service SPA Source Tree</a> — the front end this process
serves and answers</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>
@@ -0,0 +1,270 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Web Service SPA Source Tree | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Web Service SPA Source Tree | HiAPI-C# 2025 ">
<link rel="icon" href="../../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../../public/docfx.min.css">
<link rel="stylesheet" href="../../../public/main.css">
<meta name="docfx:navrel" content="../../../toc.html">
<meta name="docfx:tocrel" content="../toc.html">
<meta name="docfx:rel" content="../../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../../index.html">
<img id="logo" class="svg" src="../../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-BySource-WebserviceSpa">
<h1 id="web-service-spa-source-tree">Web Service SPA Source Tree</h1>
<p><code>HiNC-2025-webservice/wwwroot-src/src</code> is the flagship front end: a Quasar CLI single-page
application in Vue 3, TypeScript and Pinia, served by the same ASP.NET Core process that answers its
REST calls. Every page in Anatomy outside the desktop map documents something in this tree.</p>
<p>Folders below follow the stock Quasar skeleton order, with the two places the skeleton breaks called
out where they occur.</p>
<h2 id="the-one-thing-to-know-first">The One Thing to Know First</h2>
<p><code>components/</code> is grouped <strong>both ways at the same level</strong>, and that is deliberate rather than untidy:</p>
<ul>
<li><strong>Page-scoped</strong> — <code>components/controller/</code>, <code>components/execution/</code> and half of <code>components/mech/</code>
are chrome for exactly one route.</li>
<li><strong>Domain-scoped</strong> — <code>components/geom/</code>, <code>components/topo/</code>, <code>components/toolhouse/</code>,
<code>components/spindle/</code>, <code>components/workpiece/</code> and <code>components/mission/</code> are pulled in from
wherever the domain surfaces, most often the Control Tree.</li>
<li><strong>Primitives</strong> — <code>components/widgets/</code> and <code>components/panels/</code> have no domain at all and are the
most-imported folders in the application.</li>
</ul>
<p>So a component's folder does not tell a reader who mounts it. <code>components/mission/</code> has no Mission
page — <code>/mission</code> redirects into the Execution page's tree — and <code>components/workpiece/</code> holds a
single dropdown while the real workpiece editors are Control-Tree panels.</p>
<p>The second break is history. The application has been re-architected repeatedly, and because
Control-Tree node ids ride in <code>?tree=</code> links they are a public surface: every regroup <strong>adds</strong> a
migration hop rather than rewriting the last one. The residue is visible in <code>wwwroot-src/src/router/routes.ts</code>, where a large share of the table is
redirects preserving URLs from earlier architectures — a route existing there does <strong>not</strong> mean a
page exists for it.</p>
<h2 id="root-and-boot">Root and Boot</h2>
<ul>
<li><code>wwwroot-src/src/App.vue</code> — not the shell. It is a bare router view plus the once-per-load wiring
that subscribes the project store to the execution-status hub. The real shell is
<code>wwwroot-src/src/layouts/MainLayout.vue</code>. Documented in
<a class="xref" href="../../platform/session-state.html">Session State</a>.</li>
<li><code>wwwroot-src/src/boot/auth.ts</code>, <code>wwwroot-src/src/boot/i18n.ts</code>,
<code>wwwroot-src/src/boot/routine-toast.ts</code> — Quasar boot files, run once before mount and in a
declared order. The auth boot file patches the global fetch and inspects every 401, which is why
no API module carries its own 401 handling — but the redirect it can raise is conditional: it
fires only while the auth store reports the login gate enabled, and not when the router is
already on the login route, so on a build with the gate off a 401 redirects nowhere. The toast
boot file patches the shared notify helper so every toast is mirrored into the footer history
without touching a call site. Documented in
<a class="xref" href="../../platform/login-and-auth.html">Login and Authentication</a> and
<a class="xref" href="../../platform/i18n.html">Internationalization</a>.</li>
<li><code>wwwroot-src/src/layouts/MainLayout.vue</code> — the shell: menu bar, the routed page container, footer.
It also owns the mechanism every page depends on and no page implements: a project epoch, bumped
when the loaded project changes, is the keep-alive key, so a project change destroys and rebuilds
every cached page. Documented in <a class="xref" href="../../shell/main-panel.html">Main Panel</a> and
<a class="xref" href="../../platform/session-state.html">Session State</a>.</li>
</ul>
<h2 id="the-rest-edge">The REST Edge</h2>
<p><code>wwwroot-src/src/api/</code> is one thin typed module per backend controller family — functions, DTO types
and kind unions, no Vue code. This is the tightest correspondence between the two halves of the
application: each module wraps one named controller almost one-to-one.</p>
<ul>
<li><code>wwwroot-src/src/api/http.ts</code> — the shared response layer. Its own header names the modules that
deliberately bypass it, so &ldquo;every API module goes through it&rdquo; would be wrong.</li>
<li><code>wwwroot-src/src/api/index-service.ts</code> — <strong>not</strong> a barrel file. It wraps the backend's keyed object
store, which is where the <code>key</code> string threaded through the whole application comes from.
Documented in <a class="xref" href="../../conventions/dictionary-service-pattern.html">Dictionary Service Pattern</a>.</li>
</ul>
<h2 id="components">Components</h2>
<ul>
<li><code>wwwroot-src/src/components/</code> — the shared top level: <code>wwwroot-src/src/components/AppMenuBar.vue</code>,
<code>wwwroot-src/src/components/AppFooter.vue</code>, <code>wwwroot-src/src/components/RenderingCanvas.vue</code>,
<code>wwwroot-src/src/components/FileExplorer.vue</code> and <code>wwwroot-src/src/components/StlPreviewPane.vue</code>.
The canvas does not render locally: it opens a SignalR connection and paints server-rendered
frames, which is why several pages each own a canvas bound to a different backend scene.
Documented in
<a class="xref" href="../../conventions/rendering-canvas-web-service.html">Rendering Canvas on Web Service</a>,
<a href="../../util/file-explorer.html">File Explorer</a> and
<a class="xref" href="../../util/stl-preview-pane.html">STL Preview Pane</a>.</li>
<li><code>wwwroot-src/src/components/controlTree/</code> — the largest folder in the SPA by a factor of two, and
a <strong>registry-driven panel system</strong> rather than a folder of tree widgets.
<code>wwwroot-src/src/components/controlTree/itemTypes.ts</code> and its domain siblings map an item-type
string onto a panel component and a child-building function;
<code>wwwroot-src/src/components/controlTree/useControlTreeHost.ts</code> is the state machine that builds the
tree, gates a dirty selection switch, and syncs <code>?tree=</code>; the many
panel components are the editors the registry resolves. It serves <strong>three</strong> consumers, not one —
the Execution and General Setup pages each instantiate their own scoped host, while the Tool House
page reuses the identical panels and registry through a tab cascade and never touches the host.
Documented in <a class="xref" href="../../platform/control-tree.html">Control Tree</a> — the folder's own page — with
<a class="xref" href="../../execution/index.html">Execution Page</a>, <a class="xref" href="../../general-setup/index.html">General Setup Page</a> and
<a class="xref" href="../../tool-house/index.html">Tool House Page</a> for the three consumers, and
<a class="xref" href="../../execution/program.html">Program Branch</a> for the NC-program item types and panels that live in
this folder. The nineteen <code>SoftNc*</code> panels and their registry are a wave of their own, documented
under <a class="xref" href="../../general-setup/controller/index.html">Controller Branch</a>: which of them the tree mounts at all is decided
by <a class="xref" href="../../general-setup/controller/brand-matrix.html">Brand Matrix</a>, and what they share is
<a class="xref" href="../../general-setup/controller/editing-contract.html">Editing Contract</a>.</li>
<li><code>wwwroot-src/src/components/controlTree/toolhouse/</code> — the Tool House branch's panels. Easy to
confuse with <code>wwwroot-src/src/components/toolhouse/</code>: these are the panels the registry mounts,
that folder holds
the content fragments those panels embed, and the dependency runs one way only.</li>
<li><code>wwwroot-src/src/components/execution/</code> — the run cockpit's own panels, plus its <code>charts/</code>
sub-folder, the uPlot charting layer. Not purely page-local:
<code>wwwroot-src/src/components/execution/ExecutionToolBar.vue</code> is mounted by the Control Tree's
primary panel, and the spindle contours chart imports from the charts folder. Documented in
<a class="xref" href="../../execution/index.html">Execution Page</a>, <a class="xref" href="../../execution/strip-charts.html">Strip Charts</a> and
<a class="xref" href="../../execution/cycle-line-charts.html">Cycle-Line Charts</a>.</li>
<li><code>wwwroot-src/src/components/geom/</code> and <code>wwwroot-src/src/components/topo/</code> — structural twins: one
editor per kind, the same <code>modelKey</code> prop and <code>changed</code> / <code>error</code> emits, and a single kind → editor
map — <code>wwwroot-src/src/components/geom/geometryEditors.ts</code> and
<code>wwwroot-src/src/components/topo/transformerEditors.ts</code> — that is the source of truth for both the
switchboard and the Control Tree. A new kind must be registered there,
not merely dropped in the folder. &ldquo;topo&rdquo; means coordinate <strong>transformers</strong>, not mesh topology.
Documented in <a class="xref" href="../../geom/index.html">Geometry Panels</a> and
<a class="xref" href="../../geom/transformer-panel.html">Transformer Select Panel</a>.</li>
<li><code>wwwroot-src/src/components/widgets/</code> — the reusable input library: numeric, vector and matrix
inputs, the file-path input and picker, the CodeMirror text editor, the display-options and
object-management menus. Documented in <a class="xref" href="../../widget/index.html">Widgets</a>.</li>
<li><code>wwwroot-src/src/components/panels/</code> — pure layout machinery with no domain: the collapsible
expansion panel and the resizable stack whose registration contract produces the &ldquo;rows collapse in
place&rdquo; behaviour the tree pages describe. Documented in
<a class="xref" href="../../platform/control-tree.html">Control Tree</a>, whose dock rows are both expansion rows, and
<a class="xref" href="../../platform/session-state.html">Session State</a> for the keep-mounted flag that decides whether a
collapse unmounts its content.</li>
<li><code>wwwroot-src/src/components/mission/</code>, <code>.../toolhouse/</code>, <code>.../spindle/</code>, <code>.../preference/</code>,
<code>.../mech/</code>, <code>.../workpiece/</code> and <code>.../controller/</code> — the domain and page folders named above. The
<code>*Div.vue</code> suffix inside the Tool House folder is a convention carried over from the legacy Blazor
components: a Div is an embeddable content fragment with no panel chrome.</li>
</ul>
<h2 id="state-routing-and-text">State, Routing and Text</h2>
<ul>
<li><code>wwwroot-src/src/composables/</code> — three different concerns in one folder: hub access, shared domain
state, and UI mechanics. <code>wwwroot-src/src/composables/useSharedHub.ts</code> is the reference-counted connection manager
behind every hub composable, so a hub opens only while something consumes it.
<code>wwwroot-src/src/composables/useToolHouse.ts</code> and <code>wwwroot-src/src/composables/useSpindleCapability.ts</code> are module-level
singletons, not per-component instances. <code>wwwroot-src/src/composables/useViewPrefs.ts</code> stores layout state in the
browser only — it is neither in the project file nor in the server's user config. Documented in
<a class="xref" href="../../platform/session-state.html">Session State</a>.</li>
<li><code>wwwroot-src/src/stores/</code> — four Pinia stores. <code>wwwroot-src/src/stores/index.ts</code> is <strong>not</strong> a barrel; it is the
Quasar factory. Most shared state lives in <code>composables/</code> instead. Documented in
<a class="xref" href="../../platform/session-state.html">Session State</a>.</li>
<li><code>wwwroot-src/src/router/</code> — <code>wwwroot-src/src/router/routes.ts</code> is the table plus the legacy redirects, and
<code>wwwroot-src/src/router/treeRoutes.ts</code> is the load-bearing file its name understates: it holds the accumulated
chain of tree-id renames and the id → page resolver, plus the tab-name constants both the router
and the tab composable consume. A route's <code>meta.title</code> holds an i18n key, not a title.
Documented in <a class="xref" href="../../platform/tree-ids-and-routes.html">Tree Ids and Routes</a>.</li>
<li><code>wwwroot-src/src/i18n/</code> — three locales shipped together, each a list of namespace files.
<strong>English is the schema</strong>, not merely a locale: the two Chinese bundles are typed against it, so a
key present in English and missing there is a build error. Keys are split by UI region rather than
by source file, so there is no one-to-one mapping between an i18n file and a components folder.
Documented in <a class="xref" href="../../platform/i18n.html">Internationalization</a> and
<a class="xref" href="../../conventions/translation-remarks.html">Translation Remarks</a>.</li>
<li><code>wwwroot-src/src/pages/</code> — one file per route, and page size is a poor guide to importance: the
General Setup page is small because it delegates almost everything to the Control Tree dock and the
equipment canvas, while the File Explorer page is a thin wrapper around a large shared component.</li>
<li><code>wwwroot-src/src/utils/</code>, <code>wwwroot-src/src/directives/</code>, <code>wwwroot-src/src/css/</code> — small and mostly
presentational. Two severity scales exist and must not be conflated:
<code>wwwroot-src/src/utils/messageSeverity.ts</code> maps the engine's diagnostic scale, while the footer's routine severity
is a separate, shorter toast scale.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="../index.html">Anatomy by Source Directory</a> — the other two source trees, and how this
layer is meant to be entered</li>
<li><a class="xref" href="../webservice-backend/index.html">Web Service Backend Source Tree</a> — the process that
serves this application and answers its REST and hub traffic</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>