This repository has been archived on 2026-10-11. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
HiNC-2025-win-desktop-Deploy/App/wwwroot/HiAPI-docsite/anatomy/execution/mission/script-command-panel.html
T
2026-09-15 01:12:50 +08:00

301 lines
19 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Script Command Panel | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Script Command Panel | HiAPI-C# 2025 ">
<link rel="icon" href="../../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../../public/docfx.min.css">
<link rel="stylesheet" href="../../../public/main.css">
<meta name="docfx:navrel" content="../../../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../../index.html">
<img id="logo" class="svg" src="../../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="Anatomy-ScriptCommandPanel">
<h1 id="script-command-panel">Script Command Panel</h1>
<p>The key model is <a class="xref" href="../../../api/Hi.SessionCommands.ScriptCommand.html">ScriptCommand</a>.</p>
<p>The command holds two values: <a class="xref" href="../../../api/Hi.SessionCommands.ScriptCommand.html#Hi_SessionCommands_ScriptCommand_ScriptText">ScriptText</a>, a C# script, and
<a class="xref" href="../../../api/Hi.SessionCommands.ScriptCommand.html#Hi_SessionCommands_ScriptCommand_ScriptTitle">ScriptTitle</a>, the name it runs under. Both are written into
the project file, so the script travels with the project. <code>Run</code> evaluates the text against the
session shell as the globals object, and when the script returns a sequence of actions that sequence
is yielded into the run.</p>
<p>The title is what the command's tree row shows: the label is composed as <code>Script [title]</code>, falling
back to a bare <code>Script</code> while the title is empty. A newly added command starts empty — no title, no
text.</p>
<p>The panel renders on the command's own node under the Mission root of the Execution page's Control
Tree. The editor pane holds nothing but the editor: the command's move up / move down / duplicate /
delete buttons and its drag handle sit on its tree row, and the Add Command <code>+</code> sits on the row of
the list that holds it. A single-purpose kind like this one embeds its whole editor on that node
rather than growing section child nodes. Its tree checkbox only decides whether the run plays the
command; a disabled script is skipped during play — a caption above the title says so — and its
editor stays fully usable.</p>
<h2 id="layout">Layout</h2>
<ul>
<li>Head Line
<ul>
<li>Script Title Text Field
<ul>
<li>The model is <a class="xref" href="../../../api/Hi.SessionCommands.ScriptCommand.html#Hi_SessionCommands_ScriptCommand_ScriptTitle">ScriptTitle</a>.</li>
<li>Labelled &quot;Title (optional)&quot;.</li>
</ul>
</li>
<li>Autosave Indicator (web)
<ul>
<li>Shares the title row. See <a href="#saving">Saving</a>.</li>
</ul>
</li>
</ul>
</li>
<li>Script Editor Area
<ul>
<li>The model is <a class="xref" href="../../../api/Hi.SessionCommands.ScriptCommand.html#Hi_SessionCommands_ScriptCommand_ScriptText">ScriptText</a>.</li>
<li>Fills the rest of the panel.</li>
</ul>
</li>
</ul>
<h2 id="the-editor">The Editor</h2>
<p>The web editor is CodeMirror 6 running the <code>mission-script</code> mode: a stream tokenizer written for
the dialect a mission script actually is — top-level statements, not a compilation unit. It colours
keywords and boolean literals, strings (plain, verbatim <code>@&quot;&quot;</code>, interpolated <code>$&quot;&quot;</code> and <code>$@&quot;&quot;</code>),
character literals, hex and floating-point numbers, line and block comments, and it tags a call site
apart from a plain identifier. Because it is linear rather than a parser, every identifier of the
same kind gets the same colour no matter where it sits in the file.</p>
<p>The C# Lezer grammar the client also ships is not what this editor runs: its <code>compilationUnit</code> rule
rejects top-level statements, so it would recover from an error on line 1 and tag identical names
inconsistently. That grammar serves the separate <code>csharp</code> mode a <code>.cs</code> file opens in elsewhere in
the app.</p>
<p>Nothing in the highlighting is semantic. Everything the editor knows about types and members comes
from the backend, through completion.</p>
<h2 id="completion">Completion</h2>
<p>Typing an identifier — or asking explicitly — sends the whole script text plus the zero-based cursor
offset to <code>POST /api/script/completions</code>, and the response is an <code>items</code> array. Each item carries a
label, a kind already normalised to CodeMirror's own completion type names, a <code>detail</code> (the
signature, shown dim beside the label), a <code>documentation</code> (the XML <code>&lt;summary&gt;</code>, rendered to plain
text by Roslyn and shown in the popup's info pane) and an <code>insertText</code>.</p>
<p>Completion is Roslyn in-process, over the very <code>ScriptOptions</code> — the same references and imports —
that the run compiles the script with, so the session shell's members and the runtime API surface
complete for real rather than by name matching. The service prepends the synthesised
<code>using</code> / <code>using static</code> prefix those imports need and offsets the cursor by its length, so what the
list offers is what the evaluator will see. A cancelled request is answered 499, and a result that
arrives after the editor has moved on is dropped rather than shown.</p>
<p>Picking a method inserts the server-formatted call with each argument as a Tab stop, so the cursor
lands on the first parameter and Tab walks the rest; a method with no parameters inserts as <code>()</code>.
Properties, fields, types and keywords insert as their plain text — a snippet there would be noise.</p>
<h2 id="saving">Saving</h2>
<p>There is no Save button. Every keystroke in either field updates the model and schedules one save
500 ms after the last edit, and the pill beside the title reports the state: Idle, Dirty, Staging…,
Staged, or Error with the message in its tooltip. Staged means the server runtime holds the value —
its tooltip says so, because a staged script is not yet a committed project.</p>
<p>The write is a single PUT carrying the script text, the title and the content hash the last load or
save handed back. Each field is applied only when the body carries it. The response returns the new
hash to chain into the next save.</p>
<p>Two prompts guard the edges, each with three buttons because neither is a yes/no question:</p>
<ul>
<li><strong>Unsaved Changes</strong>, raised by the switch gate the panel hands the Control Tree host, which runs
it before the selection moves off the command and before every mission row op — move, duplicate,
delete, drag, Add Command. The op rebuilds the list it acted on, and that rebuild remounts the
panel whenever this command sits inside that list (a nested List included); when the panel
survives instead, the discard below has only put the autosave to sleep, and the next keystroke
resumes it on the same, still-valid path. The gate first lets a save already on the wire land,
and prompts only when edits are still staged after it: Save &amp; switch flushes and proceeds only
when that save landed — a conflict answered with Cancel leaves the edit staged and refuses the
switch; Discard reverts the editor to the last confirmed value and puts the autosave to sleep
until the next edit; Cancel keeps the selection where it is. A refused gate drops a row op and
says so in a notice (&quot;Cancelled — the open editor keeps its unsaved edits.&quot;). A row op cannot
reach the prompt while another is in flight — the host's busy gate refuses it first — but a
selection change holds no such gate, so a <code>?tree=</code> change arriving while a tree click's prompt is
up shares the pending answer instead of replacing it.</li>
<li><strong>Script changed elsewhere</strong>, raised when the server rejects the save because the hash no longer
matches — another tab, or a project reload, changed the command underneath. The rejection carries
the server's current text, title and hash, so the panel can offer Discard &amp; reload, Force
overwrite, or Cancel without a second round trip.</li>
</ul>
<p>The panel snapshots its command path at mount, and the autosave closes over that snapshot rather
than over the live selection, so a flush can never land on whichever command happens to be selected
when it fires. That path is an index-based entry path which every structural op re-points, which is
why the row ops run the gate above before their rebuild; an op on another row leaves the selection
on this editor's own row, re-indexed. After each successful save the tree relabels the node in
place instead of rebuilding the branch, which would remount the editor mid-edit.</p>
<h2 id="the-compile-gate">The Compile Gate</h2>
<p>A script that does not compile does nothing at run time: the command reports the compile failure
into the run's message stream and yields no work, so the run continues without it. Starting a run
therefore compile-checks every enabled script first — with the same script options and globals type
the evaluator uses — and refuses to start when any of them has an error, naming the first offender's
title, path, diagnostic id, message and line. The SPA's Start sends nothing to bypass that check, so
the refusal is what a user sees. The same check is also an endpoint of its own, per command and for
the whole mission.</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/mission/ScriptCommandPanel.vue</code> — this panel: the title row sharing
its line with the autosave indicator, the editor below it, the 500 ms autosave, the two
three-button prompts, the path snapshot taken at mount, the switch gate that waits for a save on
the wire and hands a second caller the pending prompt, and the change notice that relabels the
tree node.</li>
<li><code>wwwroot-src/src/components/controlTree/MissionCommandSlavePanel.vue</code> — the command node's
slave panel: mounts this editor on the entry path, forwards its switch gate to the host, and
shows the disabled caption above the title.</li>
<li><code>wwwroot-src/src/components/controlTree/ControlTreePanel.vue</code> — the tree rows: the drag handle
and the move / duplicate / delete buttons on the command's row, and the Add Command <code>+</code> on its
list's row.</li>
<li><code>wwwroot-src/src/components/widgets/TextEditor.vue</code> — the CodeMirror 6 wrapper: language and
read-only swapped through compartments, CRLF normalised on the way in so an unedited file cannot
dirty the autosave, an opt-in completion source that overrides the language's own, and a widened
monospace popup so full signatures fit before the dim detail column truncates.</li>
<li><code>wwwroot-src/src/components/widgets/missionScriptLanguage.ts</code> — the <code>mission-script</code> mode: the
keyword set, the string and character rules, numbers, comments, call-site tagging, and its own
highlight style.</li>
<li><code>wwwroot-src/src/components/mission/csharpCompletionSource.ts</code> — the completion source: fires on
an identifier prefix or an explicit request, sends the whole document with the cursor offset,
drops stale results, and applies every candidate as a snippet.</li>
<li><code>wwwroot-src/src/api/scriptCompletion.ts</code> — the typed wrapper over the completion endpoint and the
kind union that maps straight onto CodeMirror's completion types.</li>
<li><code>wwwroot-src/src/api/mission.ts</code> — the script command shape (text, title, content hash), its
loader, the save that carries the expected hash, and the conflict error that carries the server's
current copy.</li>
<li><code>wwwroot-src/src/composables/useAutoSave.ts</code> — the debounce, the state machine and the conflict
recovery behind the panel's saving, saved and error states; the discard that reverts the editor
and sleeps the autosave until the next edit, and the wait for a save already on the wire.</li>
<li><code>wwwroot-src/src/components/widgets/AutoSaveIndicator.vue</code> — the status pill beside the title.</li>
<li><code>wwwroot-src/src/components/controlTree/useControlTreeHost.ts</code> — funnels every selection change
and every mission row op through the active panel's switch gate, which is how the Unsaved
Changes prompt gets its say; selection is read-only while a row op is in flight, and a refused
gate drops the op with a notice.</li>
<li><code>wwwroot-src/src/components/controlTree/missionItemTypes.ts</code> — maps the <code>script</code> kind to this
panel and gives it no section children, so the whole editor lives on the command node.</li>
<li><code>wwwroot-src/src/i18n/en/mission.ts</code> — the panel's wording: both prompts' titles and messages,
the Save &amp; switch, Force overwrite and Discard &amp; reload buttons, and the load and save error
contexts.</li>
<li><code>wwwroot-src/src/i18n/en/common.ts</code> — the shared strings the panel borrows: the &ldquo;Title
(optional)&rdquo; label, the Cancel button both prompts carry, and the Unsaved Changes prompt's
Discard.</li>
<li><code>wwwroot-src/src/i18n/en/tree.ts</code> — the disabled caption and the notice a refused gate raises on
a row op.</li>
<li><code>Missions/ScriptCompletionController.cs</code> — <code>POST /api/script/completions</code>: a thin wrapper that
returns the items, 499 on cancellation and 500 with the message on failure.</li>
<li><code>Missions/ScriptCompletionService.cs</code> — the singleton holding one workspace, built from the same
script options the session evaluates with, and the snippet insert text it builds for methods.</li>
<li><code>Missions/ScriptCompileCheckService.cs</code> — compiles a script without running it, and walks the
mission for every enabled one; it backs the two compile-check endpoints and the pre-start gate.</li>
<li><code>Missions/MissionController.cs</code> — the single script PUT with its hash-based concurrency, the
content hash stamped into the command snapshot, and the two compile-check endpoints.</li>
<li><code>Execution/ExecutionController.cs</code> — the start gate that compile-checks the enabled scripts and
refuses to run while one of them has an error.</li>
</ul>
<h3 id="hiapi-engine">HiAPI Engine</h3>
<ul>
<li><code>HiNc/SessionCommands/ScriptCommand.cs</code> — the model: the title and text, the Program-category
catalog registration, the XML round-trip that puts the script into the project file, the
evaluation against the session shell with a compile failure reported into the run's message
stream, the returned actions yielded into the run, and the label rule that brackets the title
after the command name.</li>
</ul>
<h2 id="see-also">See Also</h2>
<ul>
<li><a class="xref" href="NcCodeCommand-panel.html">NcCodeCommand Panel</a> — the other command that stores its text in the project, and the one that ships no editor chrome</li>
<li><a href="../../../manual/run/the-other-commands.html">The Other Commands</a> — the task this command serves, beside the settings and output kinds</li>
<li><a href="index.html">Mission</a> — the rest of the command panels</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>