Archived
deploy.
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>File Explorer Page | HiAPI-C# 2025 </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="File Explorer Page | 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="">
|
||||
<h1 id="file-explorer-page">File Explorer Page</h1>
|
||||
|
||||
<p>The File Explorer page is a server-side filesystem browser covering three named roots:</p>
|
||||
<ul>
|
||||
<li><strong>AdminDirectory</strong> — server-wide admin area.</li>
|
||||
<li><strong>ProjectDirectory</strong> — the currently-loaded project directory (omitted from the root list when no project is loaded).</li>
|
||||
<li><strong>ResourceDir</strong> — the shared resource tree under <code>AdminDirectory + "Resource/"</code>.</li>
|
||||
</ul>
|
||||
<p><code>wwwroot-src/src/components/FileExplorer.vue</code> holds the whole browser — toolbar, tree, editor panel
|
||||
and preview. <code>wwwroot-src/src/pages/FileExplorerPage.vue</code> is a thin route wrapper around it, and
|
||||
<code>wwwroot-src/src/components/widgets/FileExplorerDialog.vue</code> mounts the same component in a modal as
|
||||
the app's file picker (see <a href="#picker-mode">Picker Mode</a>).</p>
|
||||
<div class="NOTE">
|
||||
<h5>Note</h5>
|
||||
<p>Absolute filesystem paths <strong>never leave the server through the File Explorer's own endpoints</strong>. The <code>/roots</code> endpoint returns only <code>{ name, displayName }</code>, and <code>Common/FileExplorerController.cs</code> routes every exception message through a scrub that replaces root prefixes with the tokens <code><Admin></code> / <code><Project></code> / <code><Resource></code> before returning it. That scrub is private to that controller, so it does not cover the STL preview endpoints the editor column also calls — see <a class="xref" href="stl-preview-pane.html">STL Preview Pane</a>.</p>
|
||||
</div>
|
||||
<h2 id="layout">Layout</h2>
|
||||
<ul>
|
||||
<li>File Explorer Page
|
||||
<ul>
|
||||
<li>Toolbar
|
||||
<ul>
|
||||
<li>Root Selector Dropdown — display-name only, sourced from <code>GET /roots</code>; a caller that locks the root gets the name as plain text instead.</li>
|
||||
<li>Up Button</li>
|
||||
<li>Path Input — editable; <code>Enter</code> navigates.</li>
|
||||
<li>Refresh / New Folder / New File / Upload Buttons.</li>
|
||||
<li>Sort Dropdown — sort key (Name / Size / Modified / Type), Ascending / Descending, and a “Folders first” checkbox.</li>
|
||||
<li>Editor Panel Toggle (<code>edit_note</code> pencil) — shows or hides the editor slave panel.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Breadcrumb Row — single-click navigation up the tree, plus a file-type filter select whenever the host supplies filters. The page supplies none, so the select belongs to picker mode.</li>
|
||||
<li>Explorer | Editor Splitter — the divider collapses to zero width while the editor panel is hidden.
|
||||
<ul>
|
||||
<li>Listing Tree (<code><q-tree></code>) — lazy: a folder lists its children the first time it is expanded. Each row is icon + name + a fixed-width size / modified tail, with the hover action strip laid over that tail so nothing reflows when the pointer enters a row. Ordering follows the toolbar's sort control.
|
||||
<ul>
|
||||
<li>Row select:
|
||||
<ul>
|
||||
<li>Directory → expand it and make it the current directory (the toolbar ops and the URL follow).</li>
|
||||
<li>Text file → load it into the editor panel; <code>.stl</code> → open the STL preview in that same slot. Both only while the panel is shown.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Row double-click on a file opens it in the editor panel — or the preview, for an <code>.stl</code> — even when the panel is hidden. This is the gesture that opens a file for editing; the action strip deliberately carries no Edit button, since a second pencil beside Rename's <code>drive_file_rename_outline</code> would be two near-identical icons meaning different things. The empty editor's own hint has not followed that decision — it reads <em>"Click a file (or its Edit action) to load it here."</em>, naming an action no row offers.</li>
|
||||
<li>Actions: Download (files), Download ZIP (folders), Extract ZIP (<code>.zip</code> only), Rename, Duplicate, Delete (double-click to confirm).</li>
|
||||
<li>Pick Column (picker mode only) — a radio or a checkbox on every pickable row, rendered as an empty cell elsewhere so unpickable rows keep the same indent.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Editor Slave Panel (right)
|
||||
<ul>
|
||||
<li>Bar — <code>Root / relative/path</code> label with a red <code>*</code> while the buffer is dirty, language select, <code>Auto Save</code> checkbox, <code>Save</code> button (disabled while Auto Save is on), close icon.</li>
|
||||
<li>Body — <code><TextEditor></code> wrapping CodeMirror 6, filling the panel.</li>
|
||||
<li>STL Preview — an <code>.stl</code> row hands this slot to <code><StlPreviewPane></code>, a server-rendered 3D view that replaces the editor's bar and body and stays up until it is closed; see <a class="xref" href="stl-preview-pane.html">STL Preview Pane</a>. The text buffer and any unsaved edits survive underneath and return when the preview closes.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="behavior">Behavior</h2>
|
||||
<ul>
|
||||
<li><strong>Path-traversal defence.</strong> Every request is resolved via <code>Path.GetFullPath</code> and validated with <code>Hi.Common.PathUtils.PathUtil.IsDescendant(root, absolute)</code> before any IO. Attempts like <code>relativePath=../outside</code> are rejected with HTTP 400.</li>
|
||||
<li><strong>Duplicate.</strong> <code>POST /copy</code> tries <code>{name}-Copy-00</code> through <code>{name}-Copy-19</code> and returns the first free slot; 400 if all 20 are taken.</li>
|
||||
<li><strong>UTF-8 without BOM.</strong> <code>WriteText</code> uses a cached <code>new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)</code> so round-tripped files do not accumulate a 3-byte BOM on every save.</li>
|
||||
<li><strong>Binary gating.</strong> <code>/read-text</code> reports a binary file rather than refusing it: it answers <code>content=null</code>, <code>isBinary=true</code>, and the panel toasts. The client decides before the round-trip — an extension set (<code>.stl</code>, <code>.zip</code>, <code>.dll</code>, <code>.exe</code>, images, <code>.pdf</code>, Office documents, <code>.sqlite</code> / <code>.db</code>, …) keeps a click on a binary row from opening the editor at all, and <code>.stl</code> is routed to the STL preview instead.</li>
|
||||
<li><strong>Line endings.</strong> The buffer is LF-normalized on load, because CodeMirror 6 joins lines with LF and would otherwise echo a normalize-only change that falsely dirties a freshly opened CRLF file. Save converts back to the style the file was read with.</li>
|
||||
<li><strong>Auto Save.</strong> Checked, the panel writes 800 ms after typing stops and the <code>Save</code> button disables. Switching files, closing the panel and leaving the page each flush a pending write first; with Auto Save off, dirty edits raise a discard confirm instead, and <code>settleEditorBeforeClose()</code> keeps the user on the page when that confirm is cancelled.</li>
|
||||
<li><strong>Per-device view prefs.</strong> Editor shown, splitter position, Auto Save and the sort spec persist in <code>localStorage</code> under <code>hinc.fileExplorer.viewPrefs.v1</code>. Nothing is written until one of them is changed, and the page's own default is editor <strong>shown</strong> — so a first visit, a private window or a cleared browser all open with the editor panel up and single-click file opening already live. Dialog instances seed the splitter, Auto Save and the sort from that blob and write those three back, but they neither read nor write the panel-visibility part: a picker always opens with the editor panel hidden, and a one-off edit inside one does not rearrange the page.</li>
|
||||
</ul>
|
||||
<h2 id="picker-mode">Picker Mode</h2>
|
||||
<p>The same component runs inside <code>wwwroot-src/src/components/widgets/FileExplorerDialog.vue</code>, which
|
||||
adds an apply bar and emits each pick as <code>"{rootName}:{relativePath}"</code>. Props steer it:
|
||||
<code>pickable</code> (<code>file</code> / <code>folder</code> / <code>any</code> / <code>none</code>) turns the pick column on, <code>multi</code> swaps radios for
|
||||
checkboxes, <code>filters</code> supplies the file-type dropdown (“All Files” is appended when the caller's
|
||||
list omits it), <code>lockRoot</code> and <code>allowedRoots</code> narrow the root switcher, and <code>mode: 'save'</code> turns the
|
||||
path input into a save target with a <code>forcedExtension</code> appended on confirm.</p>
|
||||
<p>That dialog is how the app opens and saves files: the menu bar's <code>Project ▾</code> entries, the
|
||||
<a class="xref" href="../widget/object-management-menu-button.html">Object Management Menu Button</a>, <code>FilePathInput</code>, the
|
||||
Mission command panels, the Machine Tool page, and the Mechanism Builder's Load and both Save As
|
||||
actions.</p>
|
||||
<h2 id="syntax-highlighting">Syntax Highlighting</h2>
|
||||
<p><code>wwwroot-src/src/components/widgets/TextEditor.vue</code> wraps CodeMirror 6 so the editor gets real syntax highlighting for the common project-file formats listed below.</p>
|
||||
<p>Stack:</p>
|
||||
<ul>
|
||||
<li><code>codemirror</code> + <code>@codemirror/state</code> + <code>@codemirror/view</code> (core).</li>
|
||||
<li><code>@codemirror/lang-xml</code> + <code>@codemirror/lang-json</code> + <code>@codemirror/lang-markdown</code> + <code>@replit/codemirror-lang-csharp</code> + the in-repo mission-script mode from <code>wwwroot-src/src/components/widgets/missionScriptLanguage.ts</code> (5 language modes).</li>
|
||||
<li><code>@codemirror/autocomplete</code>, wired only when a completion source is supplied.</li>
|
||||
</ul>
|
||||
<p>Props: <code>modelValue</code> (two-way), <code>language: 'xml' | 'json' | 'markdown' | 'csharp' | 'mission-script' | 'text'</code> (default <code>text</code>), <code>readonly</code>, and <code>completionSource</code> — an optional CodeMirror completion source that overrides the language-provided completions, read once at mount. Language and readonly are swapped via <code>Compartment.reconfigure</code>, so the view never has to tear down on mode change.</p>
|
||||
<p>Extension → language mapping lives in <code>wwwroot-src/src/components/widgets/editorLanguage.ts</code> (single source of truth imported by both <code>TextEditor.vue</code> and <code>FileExplorer.vue</code>):</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Language</th>
|
||||
<th>Extensions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>xml</code></td>
|
||||
<td><code>xml</code>, <code>hincproj</code>, <code>CoatingMaterial</code>, <code>CutterMaterial</code>, <code>Holder</code>, <code>WorkpieceMaterial</code>, <code>mp</code>, <code>MillingPara</code>, <code>SpindleCapability</code>, <code>StickMachiningTool</code>, <code>general-mech</code>, <code>mt</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>markdown</code></td>
|
||||
<td><code>md</code>, <code>markdown</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>json</code></td>
|
||||
<td><code>json</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>csharp</code></td>
|
||||
<td><code>cs</code>, <code>csx</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>mission-script</code></td>
|
||||
<td>none — the mode is picked from the editor's language select, and <code>wwwroot-src/src/components/mission/ScriptCommandPanel.vue</code> sets it directly on its own editor</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>text</code> (fallback)</td>
|
||||
<td>everything else, including <code>.nc</code> / <code>.ptp</code> / <code>.mpf</code> / <code>.h</code> / <code>.csv</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>The language select in the editor bar offers all six modes, so an auto-detected mode can be overridden per file.</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>
|
||||
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
|
||||
<ul>
|
||||
<li><code>wwwroot-src/src/components/FileExplorer.vue</code> — the browser itself: toolbar, sort control, breadcrumb + filter, the lazy tree with its hover actions, the editor slave panel with Auto Save, the STL preview slot, and the view prefs.</li>
|
||||
<li><code>wwwroot-src/src/pages/FileExplorerPage.vue</code> — routed page at <code>/util/file-explorer</code>; parses and mirrors the location in the URL and guards the route leave.</li>
|
||||
<li><code>wwwroot-src/src/components/widgets/FileExplorerDialog.vue</code> — modal wrapper: apply bar, save mode, <code>"{rootName}:{relativePath}"</code> picks.</li>
|
||||
<li><code>wwwroot-src/src/components/explorerSort.ts</code> — sort keys, comparators, <code>DEFAULT_SORT</code> and <code>coerceSortSpec</code> behind the Sort dropdown.</li>
|
||||
<li><code>wwwroot-src/src/components/widgets/fileFilter.ts</code> — the <code>FileFilter</code> shape driving the file-type select.</li>
|
||||
<li><code>wwwroot-src/src/utils/collapsibleSplit.ts</code> — collapses the editor divider while the panel is hidden.</li>
|
||||
<li><code>wwwroot-src/src/components/StlPreviewPane.vue</code> — the 3D preview that takes over the panel for <code>.stl</code> files, over <code>Disp/StlPreviewController.cs</code>.</li>
|
||||
<li><code>wwwroot-src/src/components/widgets/TextEditor.vue</code> — CodeMirror 6 wrapper.</li>
|
||||
<li><code>wwwroot-src/src/components/widgets/editorLanguage.ts</code> — extension → <code>EditorLanguage</code> mapping.</li>
|
||||
<li><code>wwwroot-src/src/components/widgets/missionScriptLanguage.ts</code> — the in-repo mission-script mode.</li>
|
||||
<li><code>wwwroot-src/src/api/fileExplorer.ts</code> — typed wrapper over <code>/api/file-explorer/*</code>.</li>
|
||||
<li><code>wwwroot-src/src/router/routes.ts</code> — <code>/util/file-explorer</code> entry (route name <code>util-file-explorer</code>).</li>
|
||||
<li><code>wwwroot-src/src/components/AppMenuBar.vue</code> — <code>Page → File Explorer</code> entry, below the separator that follows the three workflow pages.</li>
|
||||
<li><code>Common/NamedRootResolver.cs</code> — resolves <code>AdminDirectory</code> / <code>ProjectDirectory</code> / <code>ResourceDir</code>; shared with every other controller that reads or writes under a named root.</li>
|
||||
<li><code>Common/FileExplorerController.cs</code> — REST endpoints under <code>/api/file-explorer</code>:</li>
|
||||
</ul>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Method</th>
|
||||
<th>Path</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>GET</td>
|
||||
<td><code>/roots</code></td>
|
||||
<td>List available roots; Project is omitted when no project is loaded. Returns display names only.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GET</td>
|
||||
<td><code>/list?rootName=&relativePath=</code></td>
|
||||
<td>Directories-first, name-ascending listing; the client re-sorts it per the toolbar's sort control.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GET</td>
|
||||
<td><code>/read-text?…</code></td>
|
||||
<td>UTF-8 text; when the file is detected as binary returns <code>content=null</code>, <code>isBinary=true</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>POST</td>
|
||||
<td><code>/write-text</code></td>
|
||||
<td>Body <code>{rootName, relativePath, content}</code>; creates parents as needed.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>POST</td>
|
||||
<td><code>/create-file</code></td>
|
||||
<td>Fails if target exists.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>POST</td>
|
||||
<td><code>/create-directory</code></td>
|
||||
<td>Recursive create; no-op if already a directory.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>POST</td>
|
||||
<td><code>/rename</code></td>
|
||||
<td>Body <code>{rootName, relativePath, newRelativePath}</code>; refuses cross-root moves.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>POST</td>
|
||||
<td><code>/copy</code></td>
|
||||
<td><code>{name}-Copy-00..-Copy-19</code>; uses <code>Hi.Common.FileLines.FileUtil.CopyDirectory</code> for directories.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>DELETE</td>
|
||||
<td><code>/delete?…</code></td>
|
||||
<td>Files → delete, directories → recursive; refuses to delete the root itself.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GET</td>
|
||||
<td><code>/download?…</code></td>
|
||||
<td>Streams a file as <code>application/octet-stream</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>GET</td>
|
||||
<td><code>/download-zip?…</code></td>
|
||||
<td>Zips a directory in-memory and streams <code>application/zip</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>POST</td>
|
||||
<td><code>/upload?…</code></td>
|
||||
<td>Multipart single-file upload; overwrites target.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>POST</td>
|
||||
<td><code>/extract-zip</code></td>
|
||||
<td>Body <code>{rootName, relativePath}</code>; extracts next to the archive into <code><name>/</code>.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2 id="addressing">Addressing</h2>
|
||||
<p>The browsed location is mirrored into the URL by an optional catch-all: <code>/util/file-explorer/{RootTitle}/{relative/path}</code>. The bare <code>/util/file-explorer</code> still resolves through the named route the menu uses, so both a deep link and a plain menu click land correctly. Browsing rewrites the URL with <code>router.replace</code>, and an external URL change — a paste, a bookmark, browser back — drives the explorer the other way.</p>
|
||||
<p>The named roots (Admin / Project / Resource) are the only addressing the client sees, and the absolute path is formed and kept on the server — subject to the error-message caveat noted at the head of this page.</p>
|
||||
<h2 id="see-also">See Also</h2>
|
||||
<ul>
|
||||
<li><p><a href="../../manual/utilities/file-explorer.html">File Explorer (manual)</a> — the end-user task: the roots, and renaming a file to move it</p>
|
||||
</li>
|
||||
<li><p><a class="xref" href="../widget/object-management-menu-button.html">Object Management Menu Button</a> — shares the XML editor dialog pattern and targets the same project / resource folders through a different endpoint family.</p>
|
||||
</li>
|
||||
<li><p><a class="xref" href="mech-builder.html">Mechanism Builder Page</a> — drives its Load and both Save As operations through this browser's dialog wrapper.</p>
|
||||
</li>
|
||||
<li><p><a href="stl-preview-pane.html">STL Preview Pane</a> — the 3D preview this page's editor column hands over to for an <code>.stl</code> row.</p>
|
||||
</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,162 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Util Pages | HiAPI-C# 2025 </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Util Pages | 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="">
|
||||
<h1 id="util-pages">Util Pages</h1>
|
||||
|
||||
<p>The utility pages are the ones not tied to the main machining workflow. They support side-tasks —
|
||||
browsing the server filesystem and building a generic mechanism XML — and are reachable from the
|
||||
menu bar's <code>Page ▾</code> dropdown, below the separator that follows the three workflow pages (Tool House
|
||||
/ General Setup / Execution). Their routes are <code>/util/file-explorer</code> and <code>/util/mech-builder</code>; the
|
||||
dropdown itself is built in <code>wwwroot-src/src/components/AppMenuBar.vue</code> over the <code>menu.page.*</code>
|
||||
strings in <code>wwwroot-src/src/i18n/en/menu.ts</code>, and both routes are declared in
|
||||
<code>wwwroot-src/src/router/routes.ts</code>.</p>
|
||||
<p>The two pages share a component, not just a menu section. The File Explorer's browser
|
||||
(<code>wwwroot-src/src/components/FileExplorer.vue</code>) also runs inside a modal wrapper,
|
||||
<code>wwwroot-src/src/components/widgets/FileExplorerDialog.vue</code>, and that wrapper is the app's file
|
||||
picker: it is what the Mechanism Builder's Load and both Save As actions open, and what the menu
|
||||
bar's <code>Project ▾</code> entries open. One page documents the browser, the other a page built on it.</p>
|
||||
<h2 id="pages">Pages</h2>
|
||||
<ul>
|
||||
<li><a href="file-explorer.html">File Explorer</a> — server-side filesystem browser over three named roots (Admin
|
||||
/ Project / Resource), with a lazy-loading tree, an editor slave panel running CodeMirror 6, an
|
||||
STL preview, zip pack / extract, and rename / duplicate / delete. The same component is the app's
|
||||
modal file picker.</li>
|
||||
<li><a href="mech-builder.html">Mechanism Builder</a> — editor for a standalone <a class="xref" href="../../api/Hi.Mech.GeneralMechanism.html">GeneralMechanism</a>:
|
||||
anchor / branch graph, per-branch transformer, per-anchor geometry and display colour, with
|
||||
server-side Load and Save As through the File Explorer's picker.</li>
|
||||
<li><a href="stl-preview-pane.html">STL Preview Pane</a> — the 3D preview that takes over the File Explorer's
|
||||
editor column for an <code>.stl</code> row: its cancellable load, the rendering connection it owns, and the
|
||||
transform it can bake into the file.</li>
|
||||
<li><a class="xref" href="../legacy-controller.html">Legacy-Controller</a> — the legacy controller screen, reachable from
|
||||
<code>Page → Legacy-Controller</code> below the second separator. The General Setup page's Controller branch
|
||||
is the settings face for the SoftNcRunner; this page stays reachable while it carries settings
|
||||
that tree has no editor for.</li>
|
||||
</ul>
|
||||
<h2 id="where-neighbouring-editors-live">Where Neighbouring Editors Live</h2>
|
||||
<p>Three editors a reader might expect here are shipped surfaces of other pages:</p>
|
||||
<ul>
|
||||
<li><strong>Cutter editor</strong> — a Tool House Control-Tree branch, not a util page:
|
||||
<code>wwwroot-src/src/components/controlTree/toolhouse/ToolCutterPanel.vue</code> with its section, contour
|
||||
and flute panels, over <code>Mech/CutterController.cs</code>, reached through the <code>tool-house</code> route. The
|
||||
WPF client has <code>Mech/ToolHouse/CutterManagementPanel.xaml</code> and <code>Mech/ToolHouse/MillingCutterPanel.xaml</code>.</li>
|
||||
<li><strong>Rake-face angles</strong> — plain cutter fields: <code>radialRakeAngle_deg</code> in
|
||||
<code>wwwroot-src/src/components/controlTree/toolhouse/FluteSideContourPanel.vue</code> and
|
||||
<code>axialRakeAngle_deg</code> in
|
||||
<code>wwwroot-src/src/components/controlTree/toolhouse/FluteBottomContourPanel.vue</code>. A <code>.MillingPara</code>
|
||||
or <code>.mp</code> file opens as XML in the File Explorer's editor panel.</li>
|
||||
<li><strong>Color Index Time Chart</strong> — a strip chart on the Execution page:
|
||||
<code>wwwroot-src/src/components/execution/charts/StripIndividualChart.vue</code>, mounted by
|
||||
<code>wwwroot-src/src/pages/ExecutionPage.vue</code> under the <code>colorIndexTimeChart</code> panel flag that
|
||||
<code>Environments/ExecutionDivConfig.cs</code> persists (<code>Environments/PlayerDivConfig.cs</code> on the WPF side).
|
||||
It plots the user-picked inspecting key live per step.</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,222 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Mechanism Builder Page | HiAPI-C# 2025 </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="Mechanism Builder Page | 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-MechBuilderPage">
|
||||
<h1 id="mechanism-builder-page">Mechanism Builder Page</h1>
|
||||
|
||||
<p>The Mechanism Builder page edits a standalone <a class="xref" href="../../api/Hi.Mech.GeneralMechanism.html">GeneralMechanism</a> — its anchor topology, per-branch <code>ITransformer</code>, and per-anchor optional geometry inside a <code>Solid</code>. Unlike the project-scoped editors (Fixture / Workpiece / ToolHouse / Spindle Capability), this page is <strong>user-scoped</strong> — no project needs to be loaded.</p>
|
||||
<ul>
|
||||
<li>Key Model: <a class="xref" href="../../api/Hi.Mech.GeneralMechanism.html">GeneralMechanism</a></li>
|
||||
<li>Related Model:
|
||||
<ul>
|
||||
<li><a class="xref" href="../../api/Hi.Numerical.Xyzabc.GeneralXyzabcChain.html">GeneralXyzabcChain</a> + <a class="xref" href="../../api/Hi.NcMech.Xyzabc.GeneralXyzabcMachineTool.html">GeneralXyzabcMachineTool</a> (used by “Save As Machine Tool”)</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="layout">Layout</h2>
|
||||
<ul>
|
||||
<li>Mechanism Builder Page
|
||||
<ul>
|
||||
<li>Three Fixed Columns — equal width, separated by 1px borders, none of them draggable. Every column keeps its header pinned; the graph and the editor cards scroll inside their own region below it.</li>
|
||||
<li>Column 1: Graph
|
||||
<ul>
|
||||
<li>Title + File Menu + Add Anchor Button.
|
||||
<ul>
|
||||
<li>File Menu
|
||||
<ul>
|
||||
<li>New — discards the current mechanism and starts an empty one with a root anchor.</li>
|
||||
<li>Load… — opens the server file picker (Admin / Project / Resource) filtered to <code>*.GeneralMechanism</code> / <code>*.xml</code>; the backend parses the picked file in place and records its directory so ReLoad can re-read it. <strong>That filter matches none of the shipped mechanisms</strong>, which are named <code>.general-mech</code> beside the machine tool they belong to; the dialog's appended All Files entry is what reaches them, and the backend parses whatever is picked.</li>
|
||||
<li>ReLoad — re-parses the file last loaded (disabled until a file has been loaded). The entry is spelled <code>ReLoad</code>, with the second capital.</li>
|
||||
<li>Save As General Mechanism — writes a <code>.GeneralMechanism</code> XML to the picked server location and retargets ReLoad at it.</li>
|
||||
<li>Save As Machine Tool — wraps the mechanism in a <a class="xref" href="../../api/Hi.NcMech.Xyzabc.GeneralXyzabcMachineTool.html">GeneralXyzabcMachineTool</a> and writes a <code>.MachineTool</code> XML to the picked server location, leaving the open-mechanism pointer where it is. The envelope is the Xyzabc machine tool; the menu entry names neither that nor the file type.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Add Anchor — creates a standalone anchor, connected to nothing, and selects it.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>File Line — the loaded file's relative path, or an “unsaved” caption while the mechanism has no file.</li>
|
||||
<li>Graph — mermaid diagram of anchors (nodes) + branches (edges), scrolling inside its own region; node / edge click selects. The selected anchor is filled green and the selected branch's edge is stroked green.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Column 2: Selected Item Editor — content depends on selection:
|
||||
<ul>
|
||||
<li><strong>Anchor selected:</strong> header with a Root badge on the root anchor, an Extend button, and a delete button (double-click; absent on the root).
|
||||
<ul>
|
||||
<li>Identity Card — the anchor's Guid, an inline name field (debounced), and an “Add Branch” select that connects this anchor to the picked one.</li>
|
||||
<li>Geometry Card — a Geometry checkbox attaches or removes the anchor's <code>Solid</code>, and a badge shows the current geometry type. While attached the card carries a display-colour control and mounts the geometry editor over five kinds: <code>Box3d</code>, <code>Cylindroid</code>, <code>StlFile</code>, <a class="xref" href="../../api/Hi.Geom.TransformationGeom.html">TransformationGeom</a> and <code>GeomCombination</code>. <code>CubeTreeFile</code> and <code>ExtendedCylinder</code> are absent because the backend's <code>create-geom</code> does not construct them. <code>TransformationGeom</code> still exposes its own inner geometry and inner transformer through the nested editor.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>Branch selected:</strong> header with a delete button (double-click).
|
||||
<ul>
|
||||
<li>Identity Card — the branch's Guid, an inline name field (debounced), and the fletch → arrow anchor chips naming the two anchors it joins.</li>
|
||||
<li>Transformer Card — <a class="xref" href="../geom/transformer-panel.html">Transformer Select Panel</a> (7 transformer kinds).</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Column 3: Display — <a class="xref" href="../widget/renderingcanvas-tool-bar.html">RenderingCanvas Tool Bar</a>, a rendering / disconnected badge, and <code>RenderingCanvas</code> bound to <code>DelegateFuncDisplayee(() => MechService.GeneralMechanism as IDisplayee)</code>.</li>
|
||||
<li>Server File Picker — one <code>FileExplorerDialog</code> shared by Load and both Save As actions.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="behavior">Behavior</h2>
|
||||
<ul>
|
||||
<li><strong>New-anchor auto-naming.</strong> Newly-created anchors receive placeholder names (<code>NewAnchor-001</code>, <code>NewAnchor-002</code>, …) — the first <code>NewAnchor-{i:000}</code> no descendant anchor already carries. Inline rename is debounced 400 ms.</li>
|
||||
<li><strong>Extend.</strong> The anchor editor's Extend button creates a new anchor <em>and</em> the branch from the selected one to it in a single call, then selects the new anchor. This is the normal way a chain grows; Add Anchor leaves the new anchor unconnected.</li>
|
||||
<li><strong>Branch add filter.</strong> The “Add Branch” select inside the anchor editor filters out the selected anchor itself and anchors already directly connected, in either direction. A new branch starts on <code>NoTransform</code>.</li>
|
||||
<li><strong>Root protection.</strong> The root anchor cannot be deleted; double-click delete on any other anchor works.</li>
|
||||
<li><strong>Branch transformer swap.</strong> <code>TransformerSelectPanel</code> uses a parent-aware <code>onCreate</code> hook that calls <code>POST /branch/{id}/update-transformer</code>, so the new transformer takes effect on the next frame without a re-init round-trip. Same idiom as <a class="xref" href="../general-setup/fixture.html">Fixture Page</a>.</li>
|
||||
<li><strong>Geometry cache invalidation.</strong> Geometry edits POST to <code>anchor/{id}/refresh-geom-cache</code>, which triggers the same <code>Solid.ClearCache()</code> pattern used across the project.</li>
|
||||
<li><strong>Anchor display colour.</strong> The colour input authors an <code>#rrggbb</code> that persists inside the mechanism's <code><Solid></code> element, so every consumer of the file shows the same colour; a reset button drops the authored value and an “auto” badge marks the stable Guid-seeded fallback the 3D view otherwise renders with. <code>Solid.Display</code> reads the colour per frame, so the canvas follows without a cache refresh.</li>
|
||||
<li><strong>DelegateFuncDisplayee.</strong> The <code>RenderingCanvas</code> is wired through a delegate so edits render next frame without IndexService churn.</li>
|
||||
</ul>
|
||||
<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>
|
||||
<p>HiNC-2025-webservice (Quasar CLI SPA):</p>
|
||||
<ul>
|
||||
<li><code>wwwroot-src/src/pages/MechBuilderPage.vue</code> — routed page at <code>/util/mech-builder</code>: File menu, the three columns, the anchor / branch editors, the display-colour control, the picker wiring and the canvas.</li>
|
||||
<li><code>wwwroot-src/src/components/mech/MechBuilderGraph.vue</code> — lazy mermaid loader + click-proxy onto mermaid node / edge DOM. Mermaid is dynamic-imported so the first bundle stays slim (~1 MB raw split into its own chunk); labels are emitted quoted, which the parser requires for CJK text.</li>
|
||||
<li><code>wwwroot-src/src/components/widgets/FileExplorerDialog.vue</code> — the server file picker behind Load and both Save As actions.</li>
|
||||
<li><code>wwwroot-src/src/components/geom/GeometryEditor.vue</code> — the geometry switchboard the anchor's Geometry card mounts.</li>
|
||||
<li><code>wwwroot-src/src/components/topo/TransformerSelectPanel.vue</code> — the branch transformer switchboard.</li>
|
||||
<li><code>wwwroot-src/src/api/generalMechanism.ts</code> — typed wrapper over <code>/api/general-mechanism/*</code>, including the three server-file operations and the anchor colour endpoints.</li>
|
||||
<li><code>wwwroot-src/src/api/transformer.ts</code> — the seven transformer kinds and their <code>New</code> endpoints.</li>
|
||||
<li><code>wwwroot-src/src/api/geometry.ts</code> — <code>GeometryKind</code>, the union the anchor's allowed kinds are drawn from.</li>
|
||||
<li><code>wwwroot-src/src/utils/path.ts</code> — <code>stripDefaultMarker</code>, which sheds the <code>.default</code> ownership marker from the name offered on save.</li>
|
||||
<li><code>wwwroot-src/src/router/routes.ts</code> — <code>/util/mech-builder</code> entry (route name <code>util-mech-builder</code>).</li>
|
||||
<li><code>wwwroot-src/src/components/AppMenuBar.vue</code> — <code>Page → Mechanism Builder</code> entry.</li>
|
||||
<li><code>Mech/MechBuilder/GeneralMechanismService.cs</code> — DI singleton that holds the current mechanism + last <code>BaseDirectory</code> + <code>RelFile</code>.</li>
|
||||
<li><code>Mech/MechBuilder/GeneralMechanismController.cs</code> — <code>/api/general-mechanism/*</code> CRUD over anchors / branches / per-anchor geometry and colour; server-side Load, Reload and XML Save As for both <code>GeneralMechanism</code> and <code>GeneralXyzabcMachineTool</code> envelopes.</li>
|
||||
<li><code>Mech/MechBuilder/GeneralMechanismDisplayController.cs</code> — <code>/api/general-mechanism/display/*</code> view init and the isometric reset-view.</li>
|
||||
<li><code>Common/NamedRootResolver.cs</code> — resolves the three named roots the picker offers.</li>
|
||||
<li><code>Program.cs</code> — registers <code>GeneralMechanismService</code> as a DI singleton.</li>
|
||||
</ul>
|
||||
<h2 id="indexservice-keys">IndexService Keys</h2>
|
||||
<p>The mechanism state is persistent across pages (unlike Fixture which re-initialises on mount), so mechanism keys are <strong>not</strong> registered with <code>useCleanupHub</code>. Stale keys are harmless; they are re-pointed on the next <code>index-transformer</code> / <code>index-geom</code> call.</p>
|
||||
<ul>
|
||||
<li><code>general-mechanism.current</code> — the mechanism itself (informational; <code>DelegateFuncDisplayee</code> bypasses IndexService at render time).</li>
|
||||
<li><code>general-mechanism.branch.{guid:N}.transformer</code> — stable per-branch transformer key used by <code>TransformerSelectPanel</code>.</li>
|
||||
<li><code>general-mechanism.anchor.{guid:N}.geom</code> — stable per-anchor key for whatever geometry the anchor's <code>Solid</code> holds, used by <code>GeometryEditor</code>. <code>index-geom</code> indexes that geometry without replacing it, so re-selecting an anchor leaves a non-<code>TransformationGeom</code> geometry intact.</li>
|
||||
</ul>
|
||||
<h2 id="file-io">File IO</h2>
|
||||
<p>Load and both Save As actions go through one server file picker — the File Explorer's dialog wrapper — over the three named roots <code>AdminDirectory</code> / <code>ProjectDirectory</code> / <code>ResourceDir</code>. The XML is read and written by the backend at the picked path; nothing travels through the browser. The picker opens on the root and folder of the last successful operation, starting at <code>ResourceDir</code>, where the shipped mechanism templates live, and a save offers the current file's name with the <code>.default</code> ownership marker stripped and the target extension forced (<code>.GeneralMechanism</code> or <code>.MachineTool</code>).</p>
|
||||
<p>The File menu has New / Load… / ReLoad plus the two Save As entries; there is no plain “Save” that overwrites the last path. Saving a General Mechanism retargets ReLoad at the new file; saving a Machine Tool deliberately does not, because it is a separate file type that does not replace the open mechanism pointer.</p>
|
||||
<h2 id="see-also">See Also</h2>
|
||||
<ul>
|
||||
<li><a class="xref" href="../general-setup/fixture.html">Fixture Page</a> — parent-aware <code>onCreate</code> transformer rebind pattern reused for Branch.</li>
|
||||
<li><a class="xref" href="../geom/transformer-panel.html">Transformers</a> — the shared 7-transformer switchboard.</li>
|
||||
<li><a class="xref" href="../geom/geom-manage-control.html">Geometry Management Control</a> — embedded under the anchor editor.</li>
|
||||
<li><a class="xref" href="../general-setup/machine-tool.html">Machine Tool</a> — where a project's chain is loaded, replaced and previewed, next to this user-scoped builder.</li>
|
||||
<li><a href="file-explorer.html">File Explorer</a> — the server-side browser this page's Load and Save As dialogs are built on.</li>
|
||||
<li><a class="xref" href="../general-setup/spindle-capability.html">Spindle Capability Page</a> — the project-scoped editor with the same Load / Reload / Save As pattern.</li>
|
||||
<li><a href="../../manual/utilities/mech-builder.html">Mechanism Builder (manual)</a> — the end-user task: driving this editor, and what a machine chain built in it must be named.</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,433 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>STL Preview Pane | HiAPI-C# 2025 </title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="title" content="STL Preview Pane | 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-StlPreviewPane">
|
||||
<h1 id="stl-preview-pane">STL Preview Pane</h1>
|
||||
|
||||
<p>The STL Preview Pane is the server-rendered 3D view that takes over the File Explorer's editor
|
||||
column while an <code>.stl</code> file is open. It is neither a route nor a dialog of its own: it renders
|
||||
inside the right-hand slave panel of <code>/util/file-explorer</code>, and of the modal file picker built on
|
||||
the same browser component, replacing the text editor's bar and body for as long as an STL is
|
||||
selected. It carries its own rendering connection, its own copy of the file's triangles, and a
|
||||
Transform section that can bake a pose back into the file on disk.</p>
|
||||
<h2 id="where-it-renders">Where It Renders</h2>
|
||||
<p>The File Explorer mounts the pane on the root-relative path of the STL being previewed; the named
|
||||
root and that path are both pinned when the preview opens, so switching the explorer's root closes
|
||||
the preview rather than silently re-targeting it. Three gestures open one, and all key on the <code>stl</code>
|
||||
extension alone:</p>
|
||||
<ul>
|
||||
<li>Selecting the row, but only while the editor slave panel is showing.</li>
|
||||
<li>Submitting the file's path in the toolbar's path field, under that same panel-showing guard.</li>
|
||||
<li>Double-clicking the row header, which shows the panel first.</li>
|
||||
</ul>
|
||||
<p>Panel visibility starts from the device-local File Explorer preferences on the page and defaults to
|
||||
shown. Inside the modal picker it always starts hidden and is never written back, so there the
|
||||
double-click is the only gesture that reaches a preview until the toolbar's editor-panel toggle is
|
||||
pressed — and in a save-mode picker, where the path field names the save target rather than a place
|
||||
to navigate to, submitting it never opens a preview at all.</p>
|
||||
<p>Four gestures close one: the bar's own close button, opening a text file in the same panel, a root
|
||||
switch, and the toolbar's editor-panel toggle. The first three leave the panel showing the text
|
||||
editor; the last settles the text buffer, hides the whole panel and drops the preview together.</p>
|
||||
<h2 id="layout">Layout</h2>
|
||||
<ul>
|
||||
<li>STL Preview Pane
|
||||
<ul>
|
||||
<li>Preview Bar
|
||||
<ul>
|
||||
<li>Location Label — the root's display name and the root-relative path, ellipsized, with the
|
||||
full string as its hover title.</li>
|
||||
<li>Triangle Count Badge — an outlined badge rendered only once a load has reported a count, and
|
||||
cleared again at the start of every new load. The number is formatted for the app locale.</li>
|
||||
<li><code>Scene ▾</code> Menu — the shared display-options dropdown, carrying one checkbox here,
|
||||
<strong>Origin axes</strong>.</li>
|
||||
<li><code>View ▾</code> Menu — the shared
|
||||
<a class="xref" href="../widget/renderingcanvas-tool-bar.html">RenderingCanvas Tool Bar</a>, bound to this pane's canvas.</li>
|
||||
<li><strong>Transform</strong> Button — shows and hides the Transform section; it reads primary-coloured while
|
||||
the section is open, and starts off on every mount.</li>
|
||||
<li>Close Button — a round <code>close</code> icon titled <strong>Close preview</strong>.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Transform Section — rendered only while the Transform button is on.
|
||||
<ul>
|
||||
<li>Header Row — a <strong>Transform</strong> caption and a <strong>Save</strong> button. Save stays disabled until a load
|
||||
has published a transform key, and shows a loading state while the write is in flight.</li>
|
||||
<li>General Transform Editor — the standard transformer editor, scrolling inside a capped height
|
||||
so the canvas keeps most of the pane:
|
||||
<ul>
|
||||
<li><strong>Scale</strong> numeric field.</li>
|
||||
<li><strong>Rotation sub-transformer</strong> card — <strong>Rotation axis</strong> as three components with a normalize
|
||||
button, <strong>Angle</strong> in degrees, and <strong>Pivot</strong> in mm.</li>
|
||||
<li><strong>Translation sub-transformer</strong> card — a <strong>Translation</strong> caption over three mm components.</li>
|
||||
<li>A caption naming the composed transform as <code>T × R × scale × I</code>.</li>
|
||||
<li>Every field here commits on blur or on Enter, one write per field.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Failure Text — with no transform key held, the section reads “Failed to load STL preview.” in
|
||||
place of the editor.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Canvas Body
|
||||
<ul>
|
||||
<li>Rendering Canvas — fills the rest of the pane.</li>
|
||||
<li>Loading Overlay — a centred spinner over a dim, click-through wash. The previously loaded
|
||||
model stays visible beneath it rather than being cleared.</li>
|
||||
<li>Error Banner — pinned to the foot of the canvas, carrying the failed load's message.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="loading-a-preview">Loading a Preview</h2>
|
||||
<p>One request path serves every trigger: the canvas reporting a connection id, and any change to the
|
||||
root or the relative path. Each request clears the banner and the triangle badge, raises the
|
||||
spinner, and posts the named root and the relative path to <code>show</code>.</p>
|
||||
<p>Server side, the controller resolves the root name, re-combines it with the relative path, refuses
|
||||
a result that is not a descendant of that root, and refuses a path that does not exist. The read
|
||||
itself runs on a thread-pool task rather than the request thread, and the format is sniffed from
|
||||
the file's first two lines rather than from its extension — ASCII and binary STL both load. The
|
||||
file is opened for shared read and write, so a load does not lock it against other readers.</p>
|
||||
<p>A successful read builds the native topology, commits it into the connection's slot, and only then
|
||||
touches the engine: the displayee is re-pointed at the new composition, the camera is reset to the
|
||||
engine's home view and the render cache cleared, and the geometry the commit replaced is disposed
|
||||
afterwards, so the render thread never spends a frame on a dead reference.</p>
|
||||
<p>The response carries the triangle count, the bounding box, the connection's origin-axes flag and
|
||||
the transform key. The pane reads all of those except the bounding box, which it ignores — the
|
||||
bounding-box readout a reader sees belongs to the STL File Control's info dialog, not to this pane.</p>
|
||||
<p>A rejected load — unknown or unavailable root, a path escaping the root, a missing file, an
|
||||
unreadable file — answers a non-2xx status carrying the server's message, and the shared response
|
||||
helper folds the status into the text; that composite is what the banner shows. The message itself
|
||||
is passed through as the server wrote it: unlike the File Explorer's own endpoints, this controller
|
||||
runs no root-prefix scrub over an exception message, so a read or write that fails down in the
|
||||
filesystem layer can surface a server path in the banner.</p>
|
||||
<h2 id="cancelling-a-load">Cancelling a Load</h2>
|
||||
<p>Cancellation is the pane's defining behaviour: clicking down a folder of large STLs does not stack
|
||||
multi-second reads behind each other, and the tree stays fully interactive throughout.</p>
|
||||
<p>Three mechanisms compose:</p>
|
||||
<ul>
|
||||
<li><strong>A monotonic show token in the pane.</strong> Every request takes the next number; a response arriving
|
||||
under a stale number is discarded, and the spinner is lowered only by the newest request. Without
|
||||
it the slower of two racing round-trips would win the badge.</li>
|
||||
<li><strong>An abort controller in the pane.</strong> Each request aborts the previous one's controller before
|
||||
issuing its own, and swallows the resulting abort error. The same controller is aborted when the
|
||||
pane unmounts.</li>
|
||||
<li><strong>Supersede in the preview service.</strong> Before reading anything the controller asks the service to
|
||||
supersede the connection, which cancels whatever load is still running and installs a fresh
|
||||
cancellation source. That source doubles as the commit ticket. The request then reads under a
|
||||
token linked from the ticket and from the request-aborted token, so a browser abort and a newer
|
||||
selection cancel the same read.</li>
|
||||
</ul>
|
||||
<p>The read is cooperatively cancellable rather than interruptible: the STL reader checks the token
|
||||
once every 4096 triangles, and once every 4096 lines of the pre-scan an ASCII file needs, which
|
||||
bounds the latency to a few thousand parses rather than a whole file.</p>
|
||||
<p>The commit is gated on the ticket. A load that finishes after a newer one superseded it is refused,
|
||||
disposes the geometry it has just built, and answers HTTP 200 with a <code>canceled</code> flag; the pane drops
|
||||
that response without touching the badge or the banner, and lowers the spinner only when no newer
|
||||
request has taken the pane over in the meantime.</p>
|
||||
<h2 id="the-rendering-connection">The Rendering Connection</h2>
|
||||
<p>The pane embeds one rendering canvas, so it opens its own SignalR connection to <code>/renderingHub</code> and
|
||||
the server gives that connection its own display engine — a second live engine beside the host
|
||||
page's canvas whenever the preview is running inside the modal picker. See
|
||||
<a class="xref" href="../conventions/rendering-canvas-web-service.html">Rendering Canvas on Web Service Application</a> for the
|
||||
transport itself.</p>
|
||||
<p>The connection id is the key for everything on the server side: the display engine, and the preview
|
||||
service's per-connection slot. The canvas re-emits it after every automatic reconnect, and the pane
|
||||
answers by re-issuing the show — which is what restores the preview after a connection drop. The
|
||||
reconnected slot starts empty, so the pane re-pushes its own <strong>Origin axes</strong> preference whenever the
|
||||
server's answer disagrees with it. The pose is not restored: every commit mints a fresh identity
|
||||
transform.</p>
|
||||
<p>Release runs off the connection's death rather than off an explicit teardown call. Closing the
|
||||
preview unmounts the pane, which aborts the in-flight fetch and disconnects the canvas; the hub's
|
||||
disconnect handler disposes the engine and raises its engine-removed event; the preview service's
|
||||
subscriber then cancels any load still running, disposes the STL topology and the origin-axes
|
||||
drawing, and withdraws the transform key. That subscriber exists because a display engine disposes
|
||||
only itself — never the displayee it was pointed at.</p>
|
||||
<p>The <code>clear</code> endpoint performs most of that detach on demand — it cancels the load in flight, empties
|
||||
the canvas, disposes the topology and withdraws the transform key — but it keeps the connection's
|
||||
slot registered, with its origin-axes drawing and flag intact for the next preview; only the
|
||||
connection's death frees those. It is also the one entry in this surface that ships without a caller
|
||||
in the SPA.</p>
|
||||
<h2 id="server-side-composition">Server-Side Composition</h2>
|
||||
<p>The slot a connection owns holds a small display graph rather than a bare mesh:</p>
|
||||
<ul>
|
||||
<li><a class="xref" href="../../api/Hi.Geom.Topo.NativeTopoStl3d.html">NativeTopoStl3d</a> — the native topology built from the file's triangles, and one
|
||||
of the two objects here that have to be disposed by hand.</li>
|
||||
<li><a class="xref" href="../../api/Hi.Disp.Treat.TransformationWrapper.html">TransformationWrapper</a> — wraps that topology and carries the pose matrix.</li>
|
||||
<li><a class="xref" href="../../api/Hi.Disp.Flag.CoordinateDrawing.html">CoordinateDrawing</a> — the origin axes, created on first enable and kept across
|
||||
file switches, because the flag is a per-connection viewing preference rather than a property of
|
||||
the file. It is the other hand-disposed object, and only the connection's death frees it.</li>
|
||||
<li><a class="xref" href="../../api/Hi.Disp.DispList.html">DispList</a> — the composition assigned to <a class="xref" href="../../api/Hi.Disp.DispEngine.html#Hi_Disp_DispEngine_Displayee">Displayee</a>: the
|
||||
axes drawing, when shown, followed by the wrapper.</li>
|
||||
<li><a class="xref" href="../../api/Hi.Mech.Topo.GeneralTransform.html">GeneralTransform</a> — the pose the Transform section drives.</li>
|
||||
</ul>
|
||||
<p><a class="xref" href="../../api/Hi.Geom.Stl.html">Stl</a> is not among them. The reader's output is a local of the show request, alive only
|
||||
long enough for the native topology to copy its triangles into its own buffer and for the response
|
||||
to report the triangle count and the bounding box; the save path then mints a second one out of the
|
||||
topology's own snapshot, and it is that second object the save transforms and writes.</p>
|
||||
<p>The committed topology always holds the file's as-loaded triangles. The editor's pose lives only in
|
||||
the wrapper, which is why it can be re-applied, reset or saved repeatedly without drift.</p>
|
||||
<h2 id="transform-and-save">Transform and Save</h2>
|
||||
<p>Each committed load registers a fresh <a class="xref" href="../../api/Hi.Mech.Topo.GeneralTransform.html">GeneralTransform</a> — identity, and a new
|
||||
object rather than a reset one — in the server's keyed object store, and returns its key. The pane
|
||||
hands that key to the standard general-transform editor, the same component and the same REST
|
||||
surface the Transformer panels use; there is no preview-specific transform schema. After every
|
||||
committed edit the pane posts <code>apply-transform</code>, which re-reads the transform's matrix into the
|
||||
display wrapper and clears the render cache. That is display only — the file is untouched.</p>
|
||||
<p>Save asks for confirmation naming the root-relative path, then bakes. The service snapshots the
|
||||
native triangles under the topology's own dispose lock, multiplies them by the current matrix and
|
||||
writes the result over the previewed file as a <strong>binary</strong> STL, so an ASCII source is rewritten in
|
||||
binary form. Because the shown topology keeps the as-loaded triangles and the editor keeps its
|
||||
values, screen and disk agree afterwards — both are the original geometry times the current matrix —
|
||||
and saving twice with the same values rewrites the same file rather than compounding the pose.
|
||||
Resetting the editor's fields returns to the as-loaded pose.</p>
|
||||
<p>A save that finds the topology already disposed by a newer selection reports that it was superseded
|
||||
instead of writing. On success the pane raises a toast and the File Explorer re-lists the containing
|
||||
folder, so the row's size and modified columns catch up. Nothing else is refreshed: a
|
||||
<a class="xref" href="../../api/Hi.Geom.StlFile.html">StlFile</a> geometry elsewhere in the project that references the same path keeps the
|
||||
triangles it already loaded.</p>
|
||||
<p>Switching files re-mints the transform — a new key, an identity pose, and an editor that re-reads
|
||||
cleanly because the key changed. The old key is withdrawn from the keyed store on replacement, on
|
||||
clear and on connection death; the two derived keys the editor mints beneath it for the rotation and
|
||||
translation sub-transformers are not withdrawn with it.</p>
|
||||
<h2 id="endpoints">Endpoints</h2>
|
||||
<p>Every entry is a POST under <code>/api/stl-preview</code>, addressed by the rendering connection id. The four
|
||||
that touch the canvas resolve the display engine through the non-creating lookup, so a disconnected
|
||||
id cannot orphan a fresh engine; <code>save</code> needs no engine at all and writes straight from the slot.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Path</th>
|
||||
<th>Body</th>
|
||||
<th>Purpose</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>show/{renderingConnectionId}</code></td>
|
||||
<td><code>{ rootName, relFile }</code></td>
|
||||
<td>Supersede, read, commit, re-point the engine. Answers the triangle count, the bounding box, the origin-axes flag and the transform key — or a <code>canceled</code> flag.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>set-coordinate/{renderingConnectionId}</code></td>
|
||||
<td>a bare boolean</td>
|
||||
<td>Show or hide the origin axes. With nothing on the canvas yet it stores the flag for the next commit.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>apply-transform/{renderingConnectionId}</code></td>
|
||||
<td>none</td>
|
||||
<td>Re-read the transform's matrix into the display wrapper.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>save/{renderingConnectionId}</code></td>
|
||||
<td>none</td>
|
||||
<td>Bake the matrix into the geometry and overwrite the file.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>clear/{renderingConnectionId}</code></td>
|
||||
<td>none</td>
|
||||
<td>Cancel the load in flight, empty the canvas, free the STL geometry; the slot and its origin-axes drawing survive.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><code>rootName</code> is one of the three named roots — the admin directory, the loaded project directory and
|
||||
the shared resource directory — resolved server-side; the project root is unavailable until a
|
||||
project is loaded. Addressing is root-relative in both directions and the absolute path is formed
|
||||
and kept on the server; the unscrubbed error text noted above is the one place it can escape.</p>
|
||||
<h2 id="the-text-buffer-underneath">The Text Buffer Underneath</h2>
|
||||
<p>Opening a preview does not disturb the text editor beneath it. There is no flush, no discard prompt
|
||||
and no reload: the loaded path, the buffer, its saved snapshot and its dirty mark are all left as
|
||||
they were, and the bar's close button brings them straight back, dirty star included. Clicking a
|
||||
text file while a preview is open closes the preview first and then loads that file through the
|
||||
editor's normal open path, which settles the outgoing buffer the way any file switch does.</p>
|
||||
<p>Two consequences follow from the buffer staying live. The toolbar's editor-panel toggle remains
|
||||
reachable while the preview is up, and it settles the buffer before hiding the panel — so a dirty
|
||||
buffer in manual mode raises its discard confirm at that point, even though the visible pane is a 3D
|
||||
canvas. And an auto-save debounce armed just before the preview opened still fires underneath it.</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/components/StlPreviewPane.vue</code> — this pane: the bar, the Transform section, the
|
||||
canvas body with its overlay and banner, the show token and abort controller, and the optimistic
|
||||
origin-axes toggle.</li>
|
||||
<li><code>wwwroot-src/src/components/FileExplorer.vue</code> — the sole host: the preview slot in the editor
|
||||
slave panel, the extension test and the three opening gestures, the pinned root, the close paths,
|
||||
and the folder refresh after a save.</li>
|
||||
<li><code>wwwroot-src/src/components/widgets/FileExplorerDialog.vue</code> — the modal wrapper, whose picker
|
||||
props are what leave the editor panel hidden inside the picker.</li>
|
||||
<li><code>wwwroot-src/src/pages/FileExplorerPage.vue</code> — the routed host at <code>/util/file-explorer</code>.</li>
|
||||
<li><code>wwwroot-src/src/api/stlPreview.ts</code> — the typed wrapper over the five endpoints, the result shape,
|
||||
and the abort signal threaded into <code>show</code>.</li>
|
||||
<li><code>wwwroot-src/src/api/http.ts</code> — the shared response helper that wrapper uses: a non-2xx throws an
|
||||
error whose text is the status followed by the server's message, while a 200 body carrying
|
||||
<code>success: false</code> reaches the caller as data, which is what lets the <code>canceled</code> answer be ignored
|
||||
silently.</li>
|
||||
<li><code>wwwroot-src/src/components/RenderingCanvas.vue</code> — the connection the pane owns, its reconnect
|
||||
loop and the re-emitted connection id.</li>
|
||||
<li><code>wwwroot-src/src/components/RenderingCanvasToolBar.vue</code> — the <code>View ▾</code> menu in the bar.</li>
|
||||
<li><code>wwwroot-src/src/components/widgets/DisplayOptionsMenu.vue</code> — the <code>Scene ▾</code> dropdown; the pane
|
||||
passes one checkbox group and no label, so the button falls back to the localized “Scene”.</li>
|
||||
<li><code>wwwroot-src/src/components/topo/GeneralTransformEditor.vue</code>,
|
||||
<code>wwwroot-src/src/components/topo/StaticRotationEditor.vue</code> and
|
||||
<code>wwwroot-src/src/components/topo/StaticTranslationEditor.vue</code> — the Transform section's editor and
|
||||
its two sub-transformer cards.</li>
|
||||
<li><code>wwwroot-src/src/components/widgets/NumericInput.vue</code> and
|
||||
<code>wwwroot-src/src/components/widgets/Vec3Input.vue</code> — the fields inside them, committing on blur or
|
||||
Enter.</li>
|
||||
<li><code>wwwroot-src/src/api/transformer.ts</code>, <code>wwwroot-src/src/api/geometry.ts</code> and
|
||||
<code>wwwroot-src/src/api/index-service.ts</code> — the standard transform surface those editors write
|
||||
through.</li>
|
||||
<li><code>wwwroot-src/src/i18n/en/explorer.ts</code> — the <code>explorer.preview.*</code> strings: the close title, the
|
||||
failure text, <strong>Origin axes</strong>, <strong>Transform</strong>, and the save-confirm dialog.</li>
|
||||
<li><code>wwwroot-src/src/i18n/en/geom.ts</code> — the pluralized triangle-count phrase in the badge.</li>
|
||||
<li><code>wwwroot-src/src/i18n/en/topo.ts</code> and <code>wwwroot-src/src/i18n/en/widgets.ts</code> — the transform-editor
|
||||
captions, and the <code>Scene</code> and <code>View</code> menu labels.</li>
|
||||
<li><code>Disp/StlPreviewController.cs</code> — the five endpoints, the root resolution and descendant check, the
|
||||
off-thread read, the commit-then-swap ordering, the <code>canceled</code> answer, and the failure replies
|
||||
that pass an exception message through unaltered.</li>
|
||||
<li><code>Common/FileExplorerController.cs</code> — the root-prefix scrub over exception text that the File
|
||||
Explorer's own endpoints apply and this surface does not share.</li>
|
||||
<li><code>Disp/StlPreviewService.cs</code> — the per-connection slot: supersede and the commit ticket, the display
|
||||
composition, the origin-axes flag that survives file switches, the bake-and-write save, and the
|
||||
engine-removed handler that frees everything.</li>
|
||||
<li><code>Disp/RenderingService.cs</code> — the per-connection engine store, the non-creating lookup HTTP
|
||||
endpoints must use, and the engine-removed event.</li>
|
||||
<li><code>Disp/RenderingHub.cs</code> — the hub whose disconnect disposes the engine and raises that event.</li>
|
||||
<li><code>Common/NamedRootResolver.cs</code> — the three named roots and their availability rules.</li>
|
||||
<li><code>Common/IndexService.cs</code> — the keyed object store the transform is published in.</li>
|
||||
<li><code>Mech/Topo/GeneralTransformController.cs</code> — the standard transform endpoints, including the two
|
||||
that index the rotation and translation sub-transformers under derived keys.</li>
|
||||
</ul>
|
||||
<h3 id="hiapi-engine">HiAPI Engine</h3>
|
||||
<ul>
|
||||
<li><code>HiGeom/Geom/Stl.cs</code> — the reader: content-sniffed ASCII / binary detection, the cancellation check
|
||||
every 4096 triangles or pre-scan lines, and the binary writer the save uses.</li>
|
||||
<li><code>HiDisp/Geom/Topo/NativeTopoStl3d.cs</code> — the native topology, and the locked triangle snapshot the
|
||||
save takes, which answers nothing once the topology has been disposed.</li>
|
||||
<li><code>HiDisp/Disp/Treat/TransformationWrapper.cs</code> — the display-time pose applied to that topology.</li>
|
||||
<li><code>HiDisp/Disp/Flag/CoordinateDrawing.cs</code> — the origin-axes drawing.</li>
|
||||
<li><code>HiDisp/Disp/DispList.cs</code> — the one- or two-element composition the engine is pointed at.</li>
|
||||
<li><code>HiDisp/Disp/DispEngine.cs</code> — the engine: its displayee, its cache clear, its home view, and a
|
||||
dispose that frees the native engine alone.</li>
|
||||
<li><code>HiMech/Mech/Topo/GeneralTransform.cs</code> — the pose model, its uniform scale and its two
|
||||
sub-transformers.</li>
|
||||
</ul>
|
||||
<h2 id="see-also">See Also</h2>
|
||||
<ul>
|
||||
<li><a href="file-explorer.html">File Explorer</a> — the one screen that mounts this pane, and the editor column it
|
||||
takes over</li>
|
||||
<li><a class="xref" href="../geom/stlfile-control.html">STL File Control</a> — the geometry editor whose file picker is where this preview is
|
||||
most often met, and the surface that owns the bounding-box readout this pane does not show</li>
|
||||
<li><a class="xref" href="../widget/renderingcanvas-tool-bar.html">RenderingCanvas Tool Bar</a> — the <code>View ▾</code> and <code>Scene ▾</code> menus the preview bar embeds</li>
|
||||
<li><a class="xref" href="../conventions/rendering-canvas-web-service.html">Rendering Canvas on Web Service Application</a> — the hub connection the pane opens, and the
|
||||
engine-removed event that frees the preview's native geometry</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,31 @@
|
||||
|
||||
<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="file-explorer.html" name="" title="File Explorer">File Explorer</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="mech-builder.html" name="" title="Mechanism Builder">Mechanism Builder</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="stl-preview-pane.html" name="" title="STL Preview Pane">STL Preview Pane</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
{"items":[{"name":"Overview","href":"index.html","topicHref":"index.html"},{"name":"File Explorer","href":"file-explorer.html","topicHref":"file-explorer.html"},{"name":"Mechanism Builder","href":"mech-builder.html","topicHref":"mech-builder.html"},{"name":"STL Preview Pane","href":"stl-preview-pane.html","topicHref":"stl-preview-pane.html"}]}
|
||||
Reference in New Issue
Block a user