Archived
deploy.
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
<!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 "{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-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 mission row and the tree node show: 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 on both
|
||||
clients — no title, no text.</p>
|
||||
<p>The panel renders on the command's own node in the Mission branch of the Execution page's Control
|
||||
Tree, below the move / duplicate / delete control bar. 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 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 “Title (optional)” on the web, “Script Title” on WPF.</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>@""</code>, interpolated <code>$""</code> and <code>$@""</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><summary></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 when the Control Tree tries to move the selection off a command whose
|
||||
save is still pending: Save & switch flushes and then allows the switch, Discard drops the pending
|
||||
write, Cancel keeps the selection where it is.</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 & 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. 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="the-two-clients">The Two Clients</h2>
|
||||
<p>The desktop client edits the same command in AvalonEdit: a bold Script Title label with its text
|
||||
box, a Script Text label, and the editor with C# syntax highlighting and a line-number gutter,
|
||||
bound to the command through an attached behaviour. There is no completion popup and no autosave —
|
||||
the binding writes straight into the command as the text changes.</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, and the change notice that relabels the
|
||||
tree node.</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.</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
|
||||
through the active panel's switch gate, which is how the Unsaved Changes prompt gets its say.</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, their buttons, and the
|
||||
load and save error contexts.</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>
|
||||
Reference in New Issue
Block a user