Archived
428 lines
28 KiB
HTML
428 lines
28 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>Session State | HiAPI-C# 2025 </title>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="title" content="Session State | 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 "{query}"">
|
||
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
||
<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-SessionState">
|
||
<h1 id="session-state">Session State</h1>
|
||
|
||
<p>Session state is what the web client still holds after the user moves from one screen to the next. It
|
||
owns no route: the mechanism lives in the shell that wraps every route under <code>/</code> — a router outlet
|
||
inside a <code><keep-alive></code> keyed on a project epoch — together with four Pinia stores, a family of
|
||
module-level composables that deliberately sit outside the page lifecycle, and three browser storage
|
||
keys. Every value in the client answers the same three questions: does it survive a navigation, does
|
||
it survive a project change, and does it survive a browser reload.</p>
|
||
<h2 id="the-project-epoch">The Project Epoch</h2>
|
||
<p><code>MainLayout.vue</code> renders the routed page inside a <code><keep-alive></code> whose <code>:key</code> is <code>projectEpoch</code>. That
|
||
epoch is a plain integer <code>ref</code> declared in the layout component itself — it is not a store value, it
|
||
is not provided to anything, and nothing outside the file reads it. Two watchers in the same file are
|
||
the only writers, and each adds one to it: a watcher on the project store's <code>projectPath</code>, and a
|
||
watcher on the project store's <code>projectVersion</code>.</p>
|
||
<p>The key sits on the <code><keep-alive></code> element rather than on the page component inside it. Changing it
|
||
therefore discards the cache itself along with every page instance held in it and builds a fresh one,
|
||
which is why a page may rely on its own mount hook and never watch the project: both Control-Tree
|
||
pages call their host's <code>initialize()</code> from <code>onMounted</code>, and that runs once per project rather than
|
||
once per visit.</p>
|
||
<p>The two inputs move for different reasons.</p>
|
||
<ul>
|
||
<li><strong><code>projectPath</code></strong> is hydrated once at boot by the store's status fetch, assigned by New, Load and
|
||
Save As from the response each returns, cleared outright by Close, and driven thereafter by a
|
||
watcher inside the project store on the shared execution-status hub's status payload. A project
|
||
change made in another tab, from a different browser, or outside the browser altogether therefore
|
||
bumps the epoch here as well — and so, once, does the boot hydration on a service that already has
|
||
a project open.</li>
|
||
<li><strong><code>projectVersion</code></strong> is a counter the store raises in exactly one place: ReLoad. The backend raises
|
||
its project-changed event with the path it already had, so the broadcast cannot be told apart from
|
||
a no-op; the client-side counter is what makes a reload remount.</li>
|
||
</ul>
|
||
<p>Save on the current path moves neither, so it does not remount. The epoch only counts up, and because
|
||
it lives in the layout it starts again at zero on a browser reload — nothing reads its value, only its
|
||
transitions.</p>
|
||
<h2 id="what-a-navigation-costs">What a Navigation Costs</h2>
|
||
<p>The <code><keep-alive></code> carries no <code>include</code>, <code>exclude</code> or <code>max</code>, so every page reached under <code>/</code> is cached
|
||
until the epoch changes. Leaving a route deactivates its component instead of unmounting it, and
|
||
returning re-activates the same instance with its in-flight edits and its Control-Tree host intact.
|
||
Scroll offsets are not part of that: the router's <code>scrollBehavior</code> resolves every navigation to the
|
||
top of the page, and nothing saves an inner scroll position across a deactivation. The two routes
|
||
declared outside the layout — the login screen and the not-found catch-all — are not cached at all.</p>
|
||
<p>Three consequences the pages are written around:</p>
|
||
<ul>
|
||
<li>A cached page's watchers keep firing while another page is showing. The Control-Tree host compares
|
||
the current route name against the one its scope owns before it writes the <code>?tree=</code> query back, so
|
||
an off-screen host cannot rewrite the visible page's URL.</li>
|
||
<li>A global listener has to be dropped on deactivation, not on unmount. The Execution transport binds
|
||
its F5–F8 shortcuts on mount and on activation and removes them on deactivation and unmount, so on
|
||
any other page F5 falls back to the browser's own reload instead of starting the run.</li>
|
||
<li>The Execution, General Setup and Tool House pages each track their own activation in a flag that
|
||
feeds the rendering canvas's <code>active</code> prop. Such a page keeps its canvas mounted and its connection
|
||
open while the backend engine stops rendering for it.</li>
|
||
</ul>
|
||
<p>The File Explorer page adds a route-leave guard that settles its editor buffer — flushing a pending
|
||
auto-save, or prompting before discarding manual edits — and can refuse the navigation.</p>
|
||
<h2 id="the-stores">The Stores</h2>
|
||
<p>Four Pinia stores are created against the single Pinia instance built at boot. None of them is inside
|
||
the <code><keep-alive></code>, so a project change resets none of them; only a browser reload rebuilds them.</p>
|
||
<ul>
|
||
<li><strong>project</strong> holds <code>projectPath</code>, <code>projectVersion</code>, the admin and project directories, a loading flag
|
||
and the <code>hasProject</code> computed. Its actions post to the project endpoints and each throws a typed busy
|
||
error on HTTP 409, which the backend returns when another project file operation is already running —
|
||
the request is refused rather than queued. The store also owns the subscription to the shared
|
||
execution-status hub and the watcher that adopts the path that hub broadcasts.</li>
|
||
<li><strong>appState</strong> holds the debug flag (initialised from whether the page is served from <code>localhost</code>), the
|
||
physics-options and physics-licensed flags, the language code with the list of codes the server
|
||
offers, and the Execution division-visibility record. <code>loadServerPreferences</code>, called from the
|
||
layout's mount hook, hydrates those three groups in independently guarded steps, so a failing
|
||
endpoint leaves the rest usable. A division-flag write is applied optimistically and rolled back on
|
||
failure, with a monotonic sequence number so a late response cannot overwrite newer state. Its
|
||
<code>setMessage</code> is a forwarder into the footer's foreground channel.</li>
|
||
<li><strong>auth</strong> holds whether the login gate is enabled, whether this session is authenticated, the user
|
||
name, the version string the login screen shows, and a ready flag set once the first status probe
|
||
resolves. Every transition re-drives the global hub gate described below.</li>
|
||
<li><strong>routineProgress</strong> backs the footer's two channels. The foreground channel keeps the latest entry
|
||
plus a history capped at one hundred entries, which the footer's history button lists; the background
|
||
channel is a single in-flight job with a message and an optional fraction and keeps no history at
|
||
all, so it disappears when the job ends. A boot patch mirrors every Quasar toast into the foreground
|
||
channel, so a toast stays reviewable after it fades.</li>
|
||
</ul>
|
||
<h2 id="state-outside-the-stores">State Outside the Stores</h2>
|
||
<p>A second family of state lives at module scope in composables rather than in a store. The module is
|
||
evaluated once per page load and its <code>ref</code>s are created there, so every caller of the composable
|
||
receives the same objects and nothing in a component's lifecycle creates or destroys them.</p>
|
||
<p>Three of these hold project data and share one lifecycle idiom — <code>useSpindleCapability</code>,
|
||
<code>useSoftNcRunner</code> and <code>useToolHouse</code>. Each exposes an idempotent <code>ensureInstalled()</code> that, on its first
|
||
call only, opens a detached effect scope and watches <code>hasProject</code>: the state reloads when a project
|
||
appears and is cleared when one closes. Every panel that reads the singleton calls <code>ensureInstalled()</code>
|
||
in its own setup, and every call after the first does nothing.</p>
|
||
<p>The consequence is the one that matters when reading a panel on screen: <strong>a mounted panel is not
|
||
refreshed by a change made elsewhere.</strong> A project change unmounts and rebuilds every page, but it does
|
||
not re-read a singleton — the installer is already installed, and the watch it installed is on a
|
||
boolean that the load and new actions leave true. What each singleton holds is replaced only by an
|
||
action on the composable itself, or by a consumer that asks for it explicitly: the Tool House page's
|
||
mount hook calls the composable's <code>reload()</code>, while the spindle and controller panels call
|
||
<code>ensureInstalled()</code> alone. The controller branch is the sharpest case. Its leaf panels fetch their own
|
||
tables on mount and the tree builder re-fetches the runner snapshot it grows children from, but the
|
||
shared snapshot the panels gate on — the brand, the chain axes, the per-group presence flags and the
|
||
object key — is replaced only by a brand switch, an Object-Management install, or a close.</p>
|
||
<p>The remaining module singletons carry no project data and exist to join components that are not in one
|
||
another's tree: the Execution runtime flags the run page publishes for the nav bar, the sentence cursor
|
||
shared by the Program file panel and the syntax view, the strip-chart group's reload tick and hovered
|
||
x label, the two cycle-chart cursor marks the sim and sensor chart groups share, the parked cross-panel
|
||
line jump, the transport's shared reset flag, and the shared-hub registry with its auth gate.</p>
|
||
<h2 id="device-local-state">Device-Local State</h2>
|
||
<p>Three <code>localStorage</code> keys hold state belonging to one browser profile on one machine. None of them is
|
||
written into the project file and none is sent to the server.</p>
|
||
<ul>
|
||
<li>The tree-page view preferences key holds, per tree page, the column-visibility record and the two
|
||
left-dock row states; and, shared across both pages, the dock pixel widths, the editor row height,
|
||
the strip/step ratio, the two chart legend widths, the strip x-axis mode, the per-panel resize
|
||
weights, the Control-Tree expansion list, the last selection per page, the two runner-suit
|
||
checkboxes, and the three device-local panel switches for the 3D canvas, the CWE canvas and the
|
||
sentence syntax view. Writes are debounced, and two earlier key names are read once as migration
|
||
seeds when the current key is absent.</li>
|
||
<li>The locale key is a paint-time hint only: the boot sequence applies it before mount so the first
|
||
frame is already translated, then reconciles against the server value, which wins.</li>
|
||
<li>The File Explorer key holds whether its editor pane is shown, the pane split, the auto-save switch
|
||
and the sort order.</li>
|
||
</ul>
|
||
<p>What is <strong>not</strong> device-local is easy to mistake for it. The physics-options switch, the interface
|
||
language and the Execution division-visibility flags live in the server's <code>UserConfig</code>, held by a
|
||
singleton service and written to an XML file in the service's working directory. They are per install,
|
||
not per browser: two browsers pointed at the same service read and write the same values. The
|
||
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 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>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>What</th>
|
||
<th>Navigation</th>
|
||
<th>Project change</th>
|
||
<th>Browser reload</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>A page's own component state, including its Control-Tree host</td>
|
||
<td>survives</td>
|
||
<td>rebuilt</td>
|
||
<td>rebuilt</td>
|
||
</tr>
|
||
<tr>
|
||
<td>The four Pinia stores</td>
|
||
<td>survives</td>
|
||
<td>survives</td>
|
||
<td>rebuilt</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Module-singleton composables</td>
|
||
<td>survives</td>
|
||
<td>survives</td>
|
||
<td>rebuilt</td>
|
||
</tr>
|
||
<tr>
|
||
<td>The <code>localStorage</code> preferences</td>
|
||
<td>survives</td>
|
||
<td>survives</td>
|
||
<td>survives</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Route, <code>?tree=</code>, browsed path and tab segments</td>
|
||
<td>this is what changes</td>
|
||
<td>survives</td>
|
||
<td>survives</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Server preferences and the loaded project</td>
|
||
<td>survives</td>
|
||
<td>server-side</td>
|
||
<td>survives</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h2 id="signalr-connections">SignalR Connections</h2>
|
||
<p>Most hubs are shared singletons built by one factory: the execution-status hub, the CL strip hub, and
|
||
the four session-message sinks for shell, NC diagnostic, step diagnostic and NC-manipulation
|
||
diagnostic messages. There is one connection per hub for the whole application, reference-counted by
|
||
consumer. A component registers as a consumer when it calls the hub's composable and releases on its
|
||
own unmount; when the count reaches zero the teardown waits out a short grace window, so the
|
||
remount a project change forces — every consumer dropping and re-adding within the same render flush —
|
||
keeps its connections rather than renegotiating them.</p>
|
||
<p>The project store takes out a consumer registration on the execution-status hub when the store is
|
||
created and never releases it itself, so that hub keeps a consumer no matter which page is showing.</p>
|
||
<p>A global gate sits in front of all of them. While the optional login gate is enabled and the session is
|
||
not authenticated — and while auth status is still resolving — hubs stay idle rather than negotiating
|
||
into a 401. A logout or an intercepted 401 closes every connection; permitting again reconnects the
|
||
ones a caller had asked for, without the caller asking twice. Reconnection is a dense burst of
|
||
attempts, then a fixed cadence, then a slow tail that never gives up, and tab visibility or window
|
||
focus starts a fresh burst immediately.</p>
|
||
<p>Two kinds of connection are per instance rather than shared:</p>
|
||
<ul>
|
||
<li>Each rendering canvas opens its own connection to the rendering hub on mount and stops it on unmount.
|
||
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 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 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>
|
||
<p>A shared connection therefore closes when its last consumer has been gone longer than the grace window,
|
||
or when the auth gate shuts. A per-instance connection closes when the component owning it unmounts —
|
||
which is what a project change does to every page at once, while the shared hubs ride that same remount
|
||
out inside their grace window.</p>
|
||
<h2 id="boot-order">Boot Order</h2>
|
||
<p><code>App.vue</code> renders nothing but the router outlet and holds the wiring that must run exactly once per
|
||
page load: subscribe the project hub, then fetch project status. Both sit behind a watcher on the auth
|
||
predicate with a latch rather than behind a mount hook, because auth status may still be resolving at
|
||
first paint. The router's own guard hydrates auth status before the first navigation and fails open, so
|
||
a status hiccup cannot lock out an installation that has no login gate. The layout's mount hook then
|
||
hydrates the server preferences. A successful sign-in navigates with a full page load rather than a
|
||
router push, so the whole sequence runs again with the authentication cookie present.</p>
|
||
<h2 id="source-code-path">Source Code Path</h2>
|
||
<p>See <a class="xref" href="../index.html">HiNC App Anatomy</a> for git repository links.</p>
|
||
<h3 id="web-application">Web Application</h3>
|
||
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
|
||
<ul>
|
||
<li><code>wwwroot-src/src/layouts/MainLayout.vue</code> — the project epoch, its two watchers, the keyed
|
||
<code><keep-alive></code> around the router outlet, the footer, and the server-preference hydration call.</li>
|
||
<li><code>wwwroot-src/src/App.vue</code> — the auth-gated one-shot wiring: the project hub subscription and the
|
||
first status fetch.</li>
|
||
<li><code>wwwroot-src/src/stores/index.ts</code> — the Pinia instance the four stores are created against.</li>
|
||
<li><code>wwwroot-src/src/stores/project.ts</code> — the project path and version, the directories, the file actions
|
||
and their busy error, the hub subscription, and the watcher that adopts the broadcast path.</li>
|
||
<li><code>wwwroot-src/src/stores/appState.ts</code> — the debug and physics flags, the language state, the Execution
|
||
division config with its optimistic write and sequence guard, and the footer forwarder.</li>
|
||
<li><code>wwwroot-src/src/stores/auth.ts</code> — the login-gate state and the hub gate it drives.</li>
|
||
<li><code>wwwroot-src/src/stores/routineProgress.ts</code> — the footer's foreground history and its single live
|
||
background job.</li>
|
||
<li><code>wwwroot-src/src/composables/useViewPrefs.ts</code> — the device-local view preferences: the shape, the
|
||
defaults, the migration seeds and the debounced write.</li>
|
||
<li><code>wwwroot-src/src/composables/useSoftNcRunner.ts</code> — the controller singleton: the shared runner
|
||
snapshot, the object key, and the install-once project watch.</li>
|
||
<li><code>wwwroot-src/src/composables/useSpindleCapability.ts</code> — the spindle singleton on the same lifecycle
|
||
idiom.</li>
|
||
<li><code>wwwroot-src/src/composables/useToolHouse.ts</code> — the tool-house singleton and the coalesced reload the
|
||
page calls on mount.</li>
|
||
<li><code>wwwroot-src/src/composables/useSharedHub.ts</code> — the shared-hub factory: consumer counting, the
|
||
teardown grace window, the auth gate, the retry schedule and the focus recovery.</li>
|
||
<li><code>wwwroot-src/src/composables/useExecutionStatusHub.ts</code> — the status, cursor and message payloads the
|
||
store and the footer read.</li>
|
||
<li><code>wwwroot-src/src/composables/useClStripHub.ts</code> — the strip snapshot and update counter the charts
|
||
watch.</li>
|
||
<li><code>wwwroot-src/src/composables/useSessionSinkHub.ts</code> — the four message sinks and their
|
||
notify-and-re-pull contract.</li>
|
||
<li><code>wwwroot-src/src/composables/useCleanupHub.ts</code> — the per-instance cleanup connection and the key set
|
||
it releases on unmount.</li>
|
||
<li><code>wwwroot-src/src/composables/useExecutionTransport.ts</code> — the activation-scoped keyboard shortcuts and
|
||
the shared reset flag.</li>
|
||
<li><code>wwwroot-src/src/composables/useExecutionRuntime.ts</code> — the runtime flags the run page publishes.</li>
|
||
<li><code>wwwroot-src/src/composables/useSentenceCursor.ts</code> — the shared source cursor and its install-once
|
||
watch on step selection.</li>
|
||
<li><code>wwwroot-src/src/composables/useStripChartGroup.ts</code> — the group reload tick and the hovered x label.</li>
|
||
<li><code>wwwroot-src/src/composables/useCycleSyncMark.ts</code> — the per-group cycle-chart cursor mark the sim
|
||
and sensor charts share.</li>
|
||
<li><code>wwwroot-src/src/composables/useConversionJump.ts</code> — the parked cross-panel jump.</li>
|
||
<li><code>wwwroot-src/src/components/RenderingCanvas.vue</code> — the per-instance rendering connection, its mount
|
||
and unmount lifecycle, and the mounted guard that survives a mid-await teardown.</li>
|
||
<li><code>wwwroot-src/src/components/panels/PanelExpansion.vue</code> — the expansion row, and the keep-mounted flag
|
||
that decides whether a collapse unmounts its content.</li>
|
||
<li><code>wwwroot-src/src/components/AppFooter.vue</code> — the two footer channels and the recent-message list.</li>
|
||
<li><code>wwwroot-src/src/components/FileExplorer.vue</code> — the explorer's own device-local preference key.</li>
|
||
<li><code>wwwroot-src/src/components/controlTree/useControlTreeHost.ts</code> — the per-page host: the route-name
|
||
guard on the <code>?tree=</code> sync, the landing selection, and the cleanup registrations.</li>
|
||
<li><code>wwwroot-src/src/pages/ExecutionPage.vue</code> — the activation flag, the once-per-project initialization,
|
||
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/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>
|
||
<li><code>wwwroot-src/src/pages/LoginPage.vue</code> — the full-page navigation after a successful sign-in.</li>
|
||
<li><code>wwwroot-src/src/router/index.ts</code> — the auth guard that hydrates status before the first navigation.</li>
|
||
<li><code>wwwroot-src/src/router/routes.ts</code> — which routes render inside the layout and which do not.</li>
|
||
<li><code>wwwroot-src/src/boot/auth.ts</code> — the fetch wrapper that turns a 401 into a logged-out store and a
|
||
bounce to the login screen.</li>
|
||
<li><code>wwwroot-src/src/boot/i18n.ts</code> — the cached-locale fast path and the server reconcile.</li>
|
||
<li><code>wwwroot-src/src/boot/routine-toast.ts</code> — the toast mirror into the footer channel.</li>
|
||
<li><code>wwwroot-src/src/i18n/index.ts</code> — the locale storage key and the single place a locale change is
|
||
applied.</li>
|
||
<li><code>wwwroot-src/src/api/preference.ts</code> — the typed wrappers over the preference endpoints the app-state
|
||
store hydrates from.</li>
|
||
<li><code>Environments/ProjectController.cs</code> — the status, new, load, save, save-as, reload and close
|
||
endpoints, and the conflict reply that becomes the client's busy error.</li>
|
||
<li><code>Environments/PreferenceController.cs</code> — the endpoints behind the server-held preferences.</li>
|
||
<li><code>Environments/UserConfig.cs</code> — the persisted user configuration: the physics switch, the language
|
||
code, the graphic-cache limits, the step-present list and the Execution division config.</li>
|
||
<li><code>Environments/UserService.cs</code> — the singleton that owns that configuration and writes it to file.</li>
|
||
<li><code>Execution/ExecutionStatusService.cs</code> — the broadcast that carries a project change to every
|
||
connected browser.</li>
|
||
<li><code>Program.cs</code> — where the user-configuration service is registered and its file path fixed.</li>
|
||
</ul>
|
||
<h2 id="see-also">See Also</h2>
|
||
<ul>
|
||
<li><a class="xref" href="control-tree.html">Control Tree</a> — the per-page host this epoch destroys and rebuilds, and the selection it
|
||
restores from device-local storage</li>
|
||
<li><a class="xref" href="program-and-hosting.html">Program and Hosting</a> — how the SPA is served and hosted, and where the server-side
|
||
singletons this page reads actually live</li>
|
||
<li><a class="xref" href="../shell/main-panel.html">Main Panel</a> — the shell around the router outlet: the menu bar that fires the project
|
||
actions and the footer that shows their outcome</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>
|