Skip to content

Commit ed61e63

Browse files
committed
Add launch-mode-specific NAV behavior to first-class tool template - PR_26126_068-tool-template-launch-mode-nav
1 parent 0f067a9 commit ed61e63

10 files changed

Lines changed: 265 additions & 50 deletions

File tree

‎docs/dev/reports/playwright_v8_coverage_report.txt‎

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ Note: coverage entries are aggregated across every page/tool where coverageRepor
1313
Exercised tool entry points detected:
1414
(84%) Preview Generator V2 - exercised 19 runtime JS files
1515
(49%) Palette Manager - exercised 12 runtime JS files
16-
(89%) First-Class Tool Starter Template - exercised 10 runtime JS files
16+
(84%) First-Class Tool Starter Template - exercised 10 runtime JS files
1717
(0%) Workspace V2 - not exercised by this Playwright run
1818
(0%) Workspace Manager - not exercised by this Playwright run
1919

2020
Changed runtime JS files covered:
21-
(100%) none changed - no changed runtime JS files
21+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
22+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - executed lines 33/33; executed functions 7/8
23+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
24+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
2225

2326
Files with executed line/function counts where available:
2427
(2%) src/engine/input/ActionInputService.js - executed lines 397/397; executed functions 1/51
@@ -85,6 +88,7 @@ Files with executed line/function counts where available:
8588
(50%) tools/common/PaletteUsageService.js - executed lines 5/5; executed functions 1/2
8689
(50%) tools/palette-manager-v2/main.js - executed lines 88/88; executed functions 3/6
8790
(50%) tools/palette-manager-v2/modules/PaletteHistoryStack.js - executed lines 54/54; executed functions 5/10
91+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
8892
(50%) tools/toolRegistry.js - executed lines 425/425; executed functions 4/8
8993
(55%) tools/palette-manager-v2/modules/SwatchRow.js - executed lines 115/115; executed functions 6/11
9094
(56%) src/engine/logging/Logger.js - executed lines 56/56; executed functions 5/9
@@ -97,7 +101,6 @@ Files with executed line/function counts where available:
97101
(65%) tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js - executed lines 121/121; executed functions 15/23
98102
(67%) src/shared/number/numberUtils.js - executed lines 14/14; executed functions 2/3
99103
(67%) tools/preview-generator-v2/PreviewGeneratorV2ShellControl.js - executed lines 117/117; executed functions 8/12
100-
(67%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 69/69; executed functions 8/12
101104
(71%) tools/palette-manager-v2/controls/PaletteValidationErrorControl.js - executed lines 30/30; executed functions 5/7
102105
(73%) src/engine/theme/mount-shared-header.js - executed lines 143/143; executed functions 8/11
103106
(75%) src/engine/core/FixedTicker.js - executed lines 36/36; executed functions 3/4
@@ -146,15 +149,20 @@ Files with executed line/function counts where available:
146149
(100%) tools/preview-generator-v2/PreviewGeneratorV2Logger.js - executed lines 19/19; executed functions 5/5
147150
(100%) tools/preview-generator-v2/PreviewGeneratorV2RepoAccess.js - executed lines 21/21; executed functions 5/5
148151
(100%) tools/preview-generator-v2/PreviewGeneratorV2Ui.js - executed lines 48/48; executed functions 9/9
149-
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 42/42; executed functions 4/4
152+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
150153
(100%) tools/templates/first-class-tool-starter/js/controls/AccordionSection.js - executed lines 27/27; executed functions 5/5
151-
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 16/16; executed functions 4/4
154+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
152155
(100%) tools/templates/first-class-tool-starter/js/controls/InspectorControl.js - executed lines 8/8; executed functions 3/3
153156
(100%) tools/templates/first-class-tool-starter/js/controls/PreviewPanelControl.js - executed lines 23/23; executed functions 5/5
154157
(100%) tools/templates/first-class-tool-starter/js/services/ToolStateSerializer.js - executed lines 13/13; executed functions 3/3
155158

156159
Uncovered or low-coverage changed JS files:
157-
(100%) none changed - no changed runtime JS files
160+
(100%) none - no low-coverage changed runtime JS files
158161

159162
Changed JS files considered:
160-
(100%) none - no changed JS files
163+
(0%) tests/playwright/PreviewGeneratorV2Baseline.spec.mjs - changed JS file not collected as browser runtime coverage
164+
(0%) tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs - changed JS file not collected as browser runtime coverage
165+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - changed JS file with browser V8 coverage
166+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - changed JS file with browser V8 coverage
167+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - changed JS file with browser V8 coverage
168+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - changed JS file with browser V8 coverage
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
PR_26126_068-tool-template-launch-mode-nav
2+
3+
Scope:
4+
- Updated the official first-class tool starter at tools/templates/first-class-tool-starter/.
5+
- Updated existing Playwright coverage for the starter launch-mode NAV behavior.
6+
- No live tools, samples, schemas, roadmap, start_of_day folders, or tools/shared files were modified.
7+
8+
Changes:
9+
- Added separate tool-mode and workspace-mode NAV sections.
10+
- Default launch mode is tool mode.
11+
- ?launch=workspace switches visibility to the workspace NAV.
12+
- Tool NAV uses class tool-starter__tool__menu, aria-label Tool actions, and buttons:
13+
- Export
14+
- Copy JSON
15+
- Export toolState
16+
- Workspace NAV uses class tool-starter__workspace__menu, aria-label Workspace actions, and buttons:
17+
- Import manifest
18+
- Copy manifest
19+
- Export manifest
20+
- Added hidden-state CSS so only one NAV displays at a time.
21+
- Kept Preview Generator V2 color-match styling and src/engine/theme usage.
22+
- Preserved no tools/shared runtime dependency.
23+
- Preserved external CSS/JS only; no inline style block, inline script block, or inline event handlers were added.
24+
25+
Validation:
26+
- node --check tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js
27+
- node --check tools/templates/first-class-tool-starter/js/ToolStarterApp.js
28+
- node --check tools/templates/first-class-tool-starter/js/bootstrap.js
29+
- node --check tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js
30+
- node --check tests/playwright/PreviewGeneratorV2Baseline.spec.mjs
31+
- node --check tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs
32+
- rg check confirmed tools/templates/first-class-tool-starter/index.html has no inline style block, inline script block, or inline event handlers.
33+
- git diff --check
34+
- npm run test:workspace-v2
35+
- npx playwright test --config tools/templates/first-class-tool-starter/playwright.config.mjs --reporter=list
36+
37+
Playwright impacted: Yes
38+
- This PR changes template UI controls/interactions and launch-mode state.
39+
- npm run test:workspace-v2 passed.
40+
- Template-local Playwright passed.
41+
42+
Playwright behavior validated:
43+
- Official starter entry point defaults to tool NAV.
44+
- Official starter entry point with ?launch=workspace shows workspace NAV.
45+
- Only one launch-mode NAV is visible for each launch mode.
46+
- Tool and workspace NAV labels and button text match the required casing.
47+
- Duplicate button IDs are not present.
48+
- Existing starter accordion, required-field gating, primary action, and status clear behaviors still work.
49+
50+
Manual test notes:
51+
- Open tools/templates/first-class-tool-starter/index.html and confirm only Tool actions is visible with Export, Copy JSON, and Export toolState.
52+
- Open tools/templates/first-class-tool-starter/index.html?launch=workspace and confirm only Workspace actions is visible with Import manifest, Copy manifest, and Export manifest.
53+
- The official starter path is tools/templates/first-class-tool-starter/index.html; tools/templates/README.md designates that folder as the starter entry point.
54+
55+
Full samples smoke test:
56+
- Skipped. This PR only changes the first-class tool starter template and its targeted tests.

‎tests/playwright/PreviewGeneratorV2Baseline.spec.mjs‎

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ async function openPaletteManager(page) {
120120
returnserver;
121121
}
122122

123-
asyncfunctionopenToolTemplate(page){
123+
asyncfunctionopenToolTemplate(page,query=""){
124124
constserver=awaitstartRepoServer();
125125
awaitcoverageReporter.start(page);
126-
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html`,{waitUntil: "networkidle"});
126+
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html${query}`,{waitUntil: "networkidle"});
127127
returnserver;
128128
}
129129

@@ -466,6 +466,18 @@ test.describe("Preview Generator V2 baseline", () => {
466466
awaitexpect(page.locator("#shared-theme-header")).toBeAttached();
467467
awaitexpect(page.locator("[data-tool-starter-header]")).toContainText("First-Class Tool Starter");
468468
awaitexpect(page.locator("[data-tool-starter-summary]")).toHaveAttribute("data-tools-platform-summary-active","1");
469+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeVisible();
470+
awaitexpect(page.locator(".tool-starter__tool__menu")).toHaveAttribute("aria-label","Tool actions");
471+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeHidden();
472+
awaitexpect(page.locator("#toolExportButton")).toHaveText("Export");
473+
awaitexpect(page.locator("#toolCopyJsonButton")).toHaveText("Copy JSON");
474+
awaitexpect(page.locator("#toolExportToolStateButton")).toHaveText("Export toolState");
475+
476+
constduplicateIds=awaitpage.evaluate(()=>{
477+
constids=[...document.querySelectorAll("[id]")].map((element)=>element.id);
478+
returnids.filter((id,index)=>ids.indexOf(id)!==index);
479+
});
480+
expect(duplicateIds).toEqual([]);
469481

470482
constsharedReferences=awaitpage.evaluate(()=>[
471483
...document.querySelectorAll("script[src],link[href]")
@@ -482,17 +494,23 @@ test.describe("Preview Generator V2 baseline", () => {
482494

483495
awaitexpectAccordionToggles(page,"sourceInputContent");
484496

485-
construnButton=page.locator("#runToolButton");
486-
awaitexpect(runButton).toBeDisabled();
497+
constexportButton=page.locator("#toolExportButton");
498+
constcopyJsonButton=page.locator("#toolCopyJsonButton");
499+
constexportToolStateButton=page.locator("#toolExportToolStateButton");
500+
awaitexpect(exportButton).toBeDisabled();
501+
awaitexpect(copyJsonButton).toBeDisabled();
502+
awaitexpect(exportToolStateButton).toBeDisabled();
487503
awaitpage.locator("#sourceInput").fill("starter value");
488-
awaitexpect(runButton).toBeEnabled();
489-
awaitrunButton.click();
504+
awaitexpect(exportButton).toBeEnabled();
505+
awaitexpect(copyJsonButton).toBeEnabled();
506+
awaitexpect(exportToolStateButton).toBeEnabled();
507+
awaitexportButton.click();
490508
awaitexpect(page.locator("#statusLog")).toHaveValue(/Processedsourcevalue/);
491509
awaitpage.locator("#clearStatusButton").click();
492510
awaitexpect(page.locator("#statusLog")).toHaveValue("");
493511

494512
awaitpage.locator("#sourceInput").fill("");
495-
awaitexpect(runButton).toBeDisabled();
513+
awaitexpect(exportButton).toBeDisabled();
496514
awaitexpect(page.locator("#sourceValidationMessage")).toContainText("Input is required");
497515

498516
expect(pageErrors).toEqual([]);
@@ -501,4 +519,26 @@ test.describe("Preview Generator V2 baseline", () => {
501519
awaitserver.close();
502520
}
503521
});
522+
523+
test("launches first-class tool starter template in workspace nav mode",async({ page })=>{
524+
constserver=awaitopenToolTemplate(page,"?launch=workspace");
525+
constpageErrors=[];
526+
527+
page.on("pageerror",(error)=>{
528+
pageErrors.push(error.message);
529+
});
530+
531+
try{
532+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeHidden();
533+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeVisible();
534+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toHaveAttribute("aria-label","Workspace actions");
535+
awaitexpect(page.locator("#workspaceImportManifestButton")).toHaveText("Import manifest");
536+
awaitexpect(page.locator("#workspaceCopyManifestButton")).toHaveText("Copy manifest");
537+
awaitexpect(page.locator("#workspaceExportManifestButton")).toHaveText("Export manifest");
538+
expect(pageErrors).toEqual([]);
539+
}finally{
540+
awaitcoverageReporter.stop(page);
541+
awaitserver.close();
542+
}
543+
});
504544
});

‎tools/templates/first-class-tool-starter/index.html‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,16 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
3636
</div>
3737
</details>
3838

39-
<navclass="tool-starter__menu" aria-label="Tool actions">
40-
<buttonid="runToolButton" type="button">Run</button>
41-
<buttonid="resetToolButton" type="button">Reset</button>
42-
<buttonid="exportToolStateButton" type="button">Export toolState</button>
39+
<navclass="tool-starter__menu tool-starter__tool__menu" aria-label="Tool actions" data-launch-mode-nav="tool">
40+
<buttonid="toolExportButton" type="button">Export</button>
41+
<buttonid="toolCopyJsonButton" type="button">Copy JSON</button>
42+
<buttonid="toolExportToolStateButton" type="button">Export toolState</button>
43+
</nav>
44+
45+
<navclass="tool-starter__menu tool-starter__workspace__menu" aria-label="Workspace actions" data-launch-mode-nav="workspace" hidden>
46+
<buttonid="workspaceImportManifestButton" type="button">Import manifest</button>
47+
<buttonid="workspaceCopyManifestButton" type="button">Copy manifest</button>
48+
<buttonid="workspaceExportManifestButton" type="button">Export manifest</button>
4349
</nav>
4450

4551
<mainclass="tool-starter app-shell" data-tool-id="first-class-tool-starter">
@@ -54,7 +60,7 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
5460
<span>Source value</span>
5561
<inputid="sourceInput" type="text" autocomplete="off" placeholder="Enter a value to process">
5662
</label>
57-
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Run can process.</p>
63+
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Export can process.</p>
5864
</div>
5965
</section>
6066

‎tools/templates/first-class-tool-starter/js/ToolStarterApp.js‎

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
exportclassToolStarterApp{
2-
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog }){
2+
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog, windowRef =window}){
33
this.accordions=accordions;
44
this.actionNav=actionNav;
55
this.inspector=inspector;
@@ -8,15 +8,21 @@ export class ToolStarterApp {
88
this.shell=shell;
99
this.sourceInput=sourceInput;
1010
this.statusLog=statusLog;
11+
this.window=windowRef;
1112
}
1213

1314
start(){
1415
this.shell.mount();
1516
this.accordions.forEach((accordion)=>accordion.mount());
1617
this.actionNav.mount({
17-
onExport: ()=>this.exportToolState(),
18-
onReset: ()=>this.reset(),
19-
onRun: ()=>this.run()
18+
onToolCopyJson: ()=>{
19+
voidthis.copyJson();
20+
},
21+
onToolExport: ()=>this.run(),
22+
onToolExportToolState: ()=>this.exportToolState(),
23+
onWorkspaceCopyManifest: ()=>this.statusLog.write("Copy manifest action ready for workspace wiring."),
24+
onWorkspaceExportManifest: ()=>this.statusLog.write("Export manifest action ready for workspace wiring."),
25+
onWorkspaceImportManifest: ()=>this.statusLog.write("Import manifest action ready for workspace wiring.")
2026
});
2127
this.sourceInput.mount({
2228
onChange: ()=>this.refreshActions()
@@ -66,11 +72,37 @@ export class ToolStarterApp {
6672
this.refreshActions();
6773
}
6874

75+
asynccopyJson(){
76+
constvalidation=this.sourceInput.validate();
77+
if(!validation.valid){
78+
this.statusLog.error(validation.message);
79+
this.refreshActions();
80+
return;
81+
}
82+
83+
consttoolState=this.serializer.createToolState({sourceValue: validation.value});
84+
this.inspector.showObject(toolState);
85+
constjson=JSON.stringify(toolState,null,2);
86+
if(typeofthis.window.navigator?.clipboard?.writeText!=="function"){
87+
this.statusLog.write("toolState JSON preview written to Output Summary. Clipboard API is unavailable.");
88+
this.refreshActions();
89+
return;
90+
}
91+
92+
try{
93+
awaitthis.window.navigator.clipboard.writeText(json);
94+
this.statusLog.write("toolState JSON copied.");
95+
}catch(error){
96+
this.statusLog.error(`Copy JSON failed: ${error.message}`);
97+
}
98+
this.refreshActions();
99+
}
100+
69101
refreshActions(){
70102
consthasValue=this.sourceInput.hasValue();
71103
if(!hasValue){
72-
this.sourceInput.showMessage("Input is required before Run can process.",false);
104+
this.sourceInput.showMessage("Input is required before Export can process.",false);
73105
}
74-
this.actionNav.setRunEnabled(hasValue);
106+
this.actionNav.setToolActionsEnabled(hasValue);
75107
}
76108
}

‎tools/templates/first-class-tool-starter/js/bootstrap.js‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,22 @@ window.addEventListener("DOMContentLoaded", () => {
2929
constapp=newToolStarterApp({
3030
accordions,
3131
actionNav: newActionNavControl({
32-
runButton: requireElement("#runToolButton"),
33-
resetButton: requireElement("#resetToolButton"),
34-
exportButton: requireElement("#exportToolStateButton")
32+
toolCopyJsonButton: requireElement("#toolCopyJsonButton"),
33+
toolExportButton: requireElement("#toolExportButton"),
34+
toolExportToolStateButton: requireElement("#toolExportToolStateButton"),
35+
toolNav: requireElement(".tool-starter__tool__menu"),
36+
workspaceCopyManifestButton: requireElement("#workspaceCopyManifestButton"),
37+
workspaceExportManifestButton: requireElement("#workspaceExportManifestButton"),
38+
workspaceImportManifestButton: requireElement("#workspaceImportManifestButton"),
39+
workspaceNav: requireElement(".tool-starter__workspace__menu")
3540
}),
3641
inspector: newInspectorControl(requireElement("#inspectorOutput")),
3742
preview: newPreviewPanelControl(requireElement("#previewOutput")),
3843
serializer: newToolStateSerializer("first-class-tool-starter"),
3944
shell: newToolStarterShellControl(),
4045
sourceInput,
41-
statusLog
46+
statusLog,
47+
windowRef: window
4248
});
4349

4450
app.start();

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Add launch-mode-specific NAV behavior to first-class tool template - … · ToolboxAid/HTML-JavaScript-Gaming@ed61e63 · GitHub
Skip to content

Commit ed61e63

Browse files
committed
Add launch-mode-specific NAV behavior to first-class tool template - PR_26126_068-tool-template-launch-mode-nav
1 parent 0f067a9 commit ed61e63

10 files changed

Lines changed: 265 additions & 50 deletions

File tree

‎docs/dev/reports/playwright_v8_coverage_report.txt‎

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ Note: coverage entries are aggregated across every page/tool where coverageRepor
1313
Exercised tool entry points detected:
1414
(84%) Preview Generator V2 - exercised 19 runtime JS files
1515
(49%) Palette Manager - exercised 12 runtime JS files
16-
(89%) First-Class Tool Starter Template - exercised 10 runtime JS files
16+
(84%) First-Class Tool Starter Template - exercised 10 runtime JS files
1717
(0%) Workspace V2 - not exercised by this Playwright run
1818
(0%) Workspace Manager - not exercised by this Playwright run
1919

2020
Changed runtime JS files covered:
21-
(100%) none changed - no changed runtime JS files
21+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
22+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - executed lines 33/33; executed functions 7/8
23+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
24+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
2225

2326
Files with executed line/function counts where available:
2427
(2%) src/engine/input/ActionInputService.js - executed lines 397/397; executed functions 1/51
@@ -85,6 +88,7 @@ Files with executed line/function counts where available:
8588
(50%) tools/common/PaletteUsageService.js - executed lines 5/5; executed functions 1/2
8689
(50%) tools/palette-manager-v2/main.js - executed lines 88/88; executed functions 3/6
8790
(50%) tools/palette-manager-v2/modules/PaletteHistoryStack.js - executed lines 54/54; executed functions 5/10
91+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
8892
(50%) tools/toolRegistry.js - executed lines 425/425; executed functions 4/8
8993
(55%) tools/palette-manager-v2/modules/SwatchRow.js - executed lines 115/115; executed functions 6/11
9094
(56%) src/engine/logging/Logger.js - executed lines 56/56; executed functions 5/9
@@ -97,7 +101,6 @@ Files with executed line/function counts where available:
97101
(65%) tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js - executed lines 121/121; executed functions 15/23
98102
(67%) src/shared/number/numberUtils.js - executed lines 14/14; executed functions 2/3
99103
(67%) tools/preview-generator-v2/PreviewGeneratorV2ShellControl.js - executed lines 117/117; executed functions 8/12
100-
(67%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 69/69; executed functions 8/12
101104
(71%) tools/palette-manager-v2/controls/PaletteValidationErrorControl.js - executed lines 30/30; executed functions 5/7
102105
(73%) src/engine/theme/mount-shared-header.js - executed lines 143/143; executed functions 8/11
103106
(75%) src/engine/core/FixedTicker.js - executed lines 36/36; executed functions 3/4
@@ -146,15 +149,20 @@ Files with executed line/function counts where available:
146149
(100%) tools/preview-generator-v2/PreviewGeneratorV2Logger.js - executed lines 19/19; executed functions 5/5
147150
(100%) tools/preview-generator-v2/PreviewGeneratorV2RepoAccess.js - executed lines 21/21; executed functions 5/5
148151
(100%) tools/preview-generator-v2/PreviewGeneratorV2Ui.js - executed lines 48/48; executed functions 9/9
149-
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 42/42; executed functions 4/4
152+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
150153
(100%) tools/templates/first-class-tool-starter/js/controls/AccordionSection.js - executed lines 27/27; executed functions 5/5
151-
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 16/16; executed functions 4/4
154+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
152155
(100%) tools/templates/first-class-tool-starter/js/controls/InspectorControl.js - executed lines 8/8; executed functions 3/3
153156
(100%) tools/templates/first-class-tool-starter/js/controls/PreviewPanelControl.js - executed lines 23/23; executed functions 5/5
154157
(100%) tools/templates/first-class-tool-starter/js/services/ToolStateSerializer.js - executed lines 13/13; executed functions 3/3
155158

156159
Uncovered or low-coverage changed JS files:
157-
(100%) none changed - no changed runtime JS files
160+
(100%) none - no low-coverage changed runtime JS files
158161

159162
Changed JS files considered:
160-
(100%) none - no changed JS files
163+
(0%) tests/playwright/PreviewGeneratorV2Baseline.spec.mjs - changed JS file not collected as browser runtime coverage
164+
(0%) tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs - changed JS file not collected as browser runtime coverage
165+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - changed JS file with browser V8 coverage
166+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - changed JS file with browser V8 coverage
167+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - changed JS file with browser V8 coverage
168+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - changed JS file with browser V8 coverage
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
PR_26126_068-tool-template-launch-mode-nav
2+
3+
Scope:
4+
- Updated the official first-class tool starter at tools/templates/first-class-tool-starter/.
5+
- Updated existing Playwright coverage for the starter launch-mode NAV behavior.
6+
- No live tools, samples, schemas, roadmap, start_of_day folders, or tools/shared files were modified.
7+
8+
Changes:
9+
- Added separate tool-mode and workspace-mode NAV sections.
10+
- Default launch mode is tool mode.
11+
- ?launch=workspace switches visibility to the workspace NAV.
12+
- Tool NAV uses class tool-starter__tool__menu, aria-label Tool actions, and buttons:
13+
- Export
14+
- Copy JSON
15+
- Export toolState
16+
- Workspace NAV uses class tool-starter__workspace__menu, aria-label Workspace actions, and buttons:
17+
- Import manifest
18+
- Copy manifest
19+
- Export manifest
20+
- Added hidden-state CSS so only one NAV displays at a time.
21+
- Kept Preview Generator V2 color-match styling and src/engine/theme usage.
22+
- Preserved no tools/shared runtime dependency.
23+
- Preserved external CSS/JS only; no inline style block, inline script block, or inline event handlers were added.
24+
25+
Validation:
26+
- node --check tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js
27+
- node --check tools/templates/first-class-tool-starter/js/ToolStarterApp.js
28+
- node --check tools/templates/first-class-tool-starter/js/bootstrap.js
29+
- node --check tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js
30+
- node --check tests/playwright/PreviewGeneratorV2Baseline.spec.mjs
31+
- node --check tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs
32+
- rg check confirmed tools/templates/first-class-tool-starter/index.html has no inline style block, inline script block, or inline event handlers.
33+
- git diff --check
34+
- npm run test:workspace-v2
35+
- npx playwright test --config tools/templates/first-class-tool-starter/playwright.config.mjs --reporter=list
36+
37+
Playwright impacted: Yes
38+
- This PR changes template UI controls/interactions and launch-mode state.
39+
- npm run test:workspace-v2 passed.
40+
- Template-local Playwright passed.
41+
42+
Playwright behavior validated:
43+
- Official starter entry point defaults to tool NAV.
44+
- Official starter entry point with ?launch=workspace shows workspace NAV.
45+
- Only one launch-mode NAV is visible for each launch mode.
46+
- Tool and workspace NAV labels and button text match the required casing.
47+
- Duplicate button IDs are not present.
48+
- Existing starter accordion, required-field gating, primary action, and status clear behaviors still work.
49+
50+
Manual test notes:
51+
- Open tools/templates/first-class-tool-starter/index.html and confirm only Tool actions is visible with Export, Copy JSON, and Export toolState.
52+
- Open tools/templates/first-class-tool-starter/index.html?launch=workspace and confirm only Workspace actions is visible with Import manifest, Copy manifest, and Export manifest.
53+
- The official starter path is tools/templates/first-class-tool-starter/index.html; tools/templates/README.md designates that folder as the starter entry point.
54+
55+
Full samples smoke test:
56+
- Skipped. This PR only changes the first-class tool starter template and its targeted tests.

‎tests/playwright/PreviewGeneratorV2Baseline.spec.mjs‎

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ async function openPaletteManager(page) {
120120
returnserver;
121121
}
122122

123-
asyncfunctionopenToolTemplate(page){
123+
asyncfunctionopenToolTemplate(page,query=""){
124124
constserver=awaitstartRepoServer();
125125
awaitcoverageReporter.start(page);
126-
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html`,{waitUntil: "networkidle"});
126+
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html${query}`,{waitUntil: "networkidle"});
127127
returnserver;
128128
}
129129

@@ -466,6 +466,18 @@ test.describe("Preview Generator V2 baseline", () => {
466466
awaitexpect(page.locator("#shared-theme-header")).toBeAttached();
467467
awaitexpect(page.locator("[data-tool-starter-header]")).toContainText("First-Class Tool Starter");
468468
awaitexpect(page.locator("[data-tool-starter-summary]")).toHaveAttribute("data-tools-platform-summary-active","1");
469+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeVisible();
470+
awaitexpect(page.locator(".tool-starter__tool__menu")).toHaveAttribute("aria-label","Tool actions");
471+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeHidden();
472+
awaitexpect(page.locator("#toolExportButton")).toHaveText("Export");
473+
awaitexpect(page.locator("#toolCopyJsonButton")).toHaveText("Copy JSON");
474+
awaitexpect(page.locator("#toolExportToolStateButton")).toHaveText("Export toolState");
475+
476+
constduplicateIds=awaitpage.evaluate(()=>{
477+
constids=[...document.querySelectorAll("[id]")].map((element)=>element.id);
478+
returnids.filter((id,index)=>ids.indexOf(id)!==index);
479+
});
480+
expect(duplicateIds).toEqual([]);
469481

470482
constsharedReferences=awaitpage.evaluate(()=>[
471483
...document.querySelectorAll("script[src],link[href]")
@@ -482,17 +494,23 @@ test.describe("Preview Generator V2 baseline", () => {
482494

483495
awaitexpectAccordionToggles(page,"sourceInputContent");
484496

485-
construnButton=page.locator("#runToolButton");
486-
awaitexpect(runButton).toBeDisabled();
497+
constexportButton=page.locator("#toolExportButton");
498+
constcopyJsonButton=page.locator("#toolCopyJsonButton");
499+
constexportToolStateButton=page.locator("#toolExportToolStateButton");
500+
awaitexpect(exportButton).toBeDisabled();
501+
awaitexpect(copyJsonButton).toBeDisabled();
502+
awaitexpect(exportToolStateButton).toBeDisabled();
487503
awaitpage.locator("#sourceInput").fill("starter value");
488-
awaitexpect(runButton).toBeEnabled();
489-
awaitrunButton.click();
504+
awaitexpect(exportButton).toBeEnabled();
505+
awaitexpect(copyJsonButton).toBeEnabled();
506+
awaitexpect(exportToolStateButton).toBeEnabled();
507+
awaitexportButton.click();
490508
awaitexpect(page.locator("#statusLog")).toHaveValue(/Processedsourcevalue/);
491509
awaitpage.locator("#clearStatusButton").click();
492510
awaitexpect(page.locator("#statusLog")).toHaveValue("");
493511

494512
awaitpage.locator("#sourceInput").fill("");
495-
awaitexpect(runButton).toBeDisabled();
513+
awaitexpect(exportButton).toBeDisabled();
496514
awaitexpect(page.locator("#sourceValidationMessage")).toContainText("Input is required");
497515

498516
expect(pageErrors).toEqual([]);
@@ -501,4 +519,26 @@ test.describe("Preview Generator V2 baseline", () => {
501519
awaitserver.close();
502520
}
503521
});
522+
523+
test("launches first-class tool starter template in workspace nav mode",async({ page })=>{
524+
constserver=awaitopenToolTemplate(page,"?launch=workspace");
525+
constpageErrors=[];
526+
527+
page.on("pageerror",(error)=>{
528+
pageErrors.push(error.message);
529+
});
530+
531+
try{
532+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeHidden();
533+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeVisible();
534+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toHaveAttribute("aria-label","Workspace actions");
535+
awaitexpect(page.locator("#workspaceImportManifestButton")).toHaveText("Import manifest");
536+
awaitexpect(page.locator("#workspaceCopyManifestButton")).toHaveText("Copy manifest");
537+
awaitexpect(page.locator("#workspaceExportManifestButton")).toHaveText("Export manifest");
538+
expect(pageErrors).toEqual([]);
539+
}finally{
540+
awaitcoverageReporter.stop(page);
541+
awaitserver.close();
542+
}
543+
});
504544
});

‎tools/templates/first-class-tool-starter/index.html‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,16 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
3636
</div>
3737
</details>
3838

39-
<navclass="tool-starter__menu" aria-label="Tool actions">
40-
<buttonid="runToolButton" type="button">Run</button>
41-
<buttonid="resetToolButton" type="button">Reset</button>
42-
<buttonid="exportToolStateButton" type="button">Export toolState</button>
39+
<navclass="tool-starter__menu tool-starter__tool__menu" aria-label="Tool actions" data-launch-mode-nav="tool">
40+
<buttonid="toolExportButton" type="button">Export</button>
41+
<buttonid="toolCopyJsonButton" type="button">Copy JSON</button>
42+
<buttonid="toolExportToolStateButton" type="button">Export toolState</button>
43+
</nav>
44+
45+
<navclass="tool-starter__menu tool-starter__workspace__menu" aria-label="Workspace actions" data-launch-mode-nav="workspace" hidden>
46+
<buttonid="workspaceImportManifestButton" type="button">Import manifest</button>
47+
<buttonid="workspaceCopyManifestButton" type="button">Copy manifest</button>
48+
<buttonid="workspaceExportManifestButton" type="button">Export manifest</button>
4349
</nav>
4450

4551
<mainclass="tool-starter app-shell" data-tool-id="first-class-tool-starter">
@@ -54,7 +60,7 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
5460
<span>Source value</span>
5561
<inputid="sourceInput" type="text" autocomplete="off" placeholder="Enter a value to process">
5662
</label>
57-
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Run can process.</p>
63+
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Export can process.</p>
5864
</div>
5965
</section>
6066

‎tools/templates/first-class-tool-starter/js/ToolStarterApp.js‎

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
exportclassToolStarterApp{
2-
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog }){
2+
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog, windowRef =window}){
33
this.accordions=accordions;
44
this.actionNav=actionNav;
55
this.inspector=inspector;
@@ -8,15 +8,21 @@ export class ToolStarterApp {
88
this.shell=shell;
99
this.sourceInput=sourceInput;
1010
this.statusLog=statusLog;
11+
this.window=windowRef;
1112
}
1213

1314
start(){
1415
this.shell.mount();
1516
this.accordions.forEach((accordion)=>accordion.mount());
1617
this.actionNav.mount({
17-
onExport: ()=>this.exportToolState(),
18-
onReset: ()=>this.reset(),
19-
onRun: ()=>this.run()
18+
onToolCopyJson: ()=>{
19+
voidthis.copyJson();
20+
},
21+
onToolExport: ()=>this.run(),
22+
onToolExportToolState: ()=>this.exportToolState(),
23+
onWorkspaceCopyManifest: ()=>this.statusLog.write("Copy manifest action ready for workspace wiring."),
24+
onWorkspaceExportManifest: ()=>this.statusLog.write("Export manifest action ready for workspace wiring."),
25+
onWorkspaceImportManifest: ()=>this.statusLog.write("Import manifest action ready for workspace wiring.")
2026
});
2127
this.sourceInput.mount({
2228
onChange: ()=>this.refreshActions()
@@ -66,11 +72,37 @@ export class ToolStarterApp {
6672
this.refreshActions();
6773
}
6874

75+
asynccopyJson(){
76+
constvalidation=this.sourceInput.validate();
77+
if(!validation.valid){
78+
this.statusLog.error(validation.message);
79+
this.refreshActions();
80+
return;
81+
}
82+
83+
consttoolState=this.serializer.createToolState({sourceValue: validation.value});
84+
this.inspector.showObject(toolState);
85+
constjson=JSON.stringify(toolState,null,2);
86+
if(typeofthis.window.navigator?.clipboard?.writeText!=="function"){
87+
this.statusLog.write("toolState JSON preview written to Output Summary. Clipboard API is unavailable.");
88+
this.refreshActions();
89+
return;
90+
}
91+
92+
try{
93+
awaitthis.window.navigator.clipboard.writeText(json);
94+
this.statusLog.write("toolState JSON copied.");
95+
}catch(error){
96+
this.statusLog.error(`Copy JSON failed: ${error.message}`);
97+
}
98+
this.refreshActions();
99+
}
100+
69101
refreshActions(){
70102
consthasValue=this.sourceInput.hasValue();
71103
if(!hasValue){
72-
this.sourceInput.showMessage("Input is required before Run can process.",false);
104+
this.sourceInput.showMessage("Input is required before Export can process.",false);
73105
}
74-
this.actionNav.setRunEnabled(hasValue);
106+
this.actionNav.setToolActionsEnabled(hasValue);
75107
}
76108
}

‎tools/templates/first-class-tool-starter/js/bootstrap.js‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,22 @@ window.addEventListener("DOMContentLoaded", () => {
2929
constapp=newToolStarterApp({
3030
accordions,
3131
actionNav: newActionNavControl({
32-
runButton: requireElement("#runToolButton"),
33-
resetButton: requireElement("#resetToolButton"),
34-
exportButton: requireElement("#exportToolStateButton")
32+
toolCopyJsonButton: requireElement("#toolCopyJsonButton"),
33+
toolExportButton: requireElement("#toolExportButton"),
34+
toolExportToolStateButton: requireElement("#toolExportToolStateButton"),
35+
toolNav: requireElement(".tool-starter__tool__menu"),
36+
workspaceCopyManifestButton: requireElement("#workspaceCopyManifestButton"),
37+
workspaceExportManifestButton: requireElement("#workspaceExportManifestButton"),
38+
workspaceImportManifestButton: requireElement("#workspaceImportManifestButton"),
39+
workspaceNav: requireElement(".tool-starter__workspace__menu")
3540
}),
3641
inspector: newInspectorControl(requireElement("#inspectorOutput")),
3742
preview: newPreviewPanelControl(requireElement("#previewOutput")),
3843
serializer: newToolStateSerializer("first-class-tool-starter"),
3944
shell: newToolStarterShellControl(),
4045
sourceInput,
41-
statusLog
46+
statusLog,
47+
windowRef: window
4248
});
4349

4450
app.start();

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add launch-mode-specific NAV behavior to first-class tool template - … · ToolboxAid/HTML-JavaScript-Gaming@ed61e63 · GitHub
Skip to content

Commit ed61e63

Browse files
committed
Add launch-mode-specific NAV behavior to first-class tool template - PR_26126_068-tool-template-launch-mode-nav
1 parent 0f067a9 commit ed61e63

10 files changed

Lines changed: 265 additions & 50 deletions

File tree

‎docs/dev/reports/playwright_v8_coverage_report.txt‎

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ Note: coverage entries are aggregated across every page/tool where coverageRepor
1313
Exercised tool entry points detected:
1414
(84%) Preview Generator V2 - exercised 19 runtime JS files
1515
(49%) Palette Manager - exercised 12 runtime JS files
16-
(89%) First-Class Tool Starter Template - exercised 10 runtime JS files
16+
(84%) First-Class Tool Starter Template - exercised 10 runtime JS files
1717
(0%) Workspace V2 - not exercised by this Playwright run
1818
(0%) Workspace Manager - not exercised by this Playwright run
1919

2020
Changed runtime JS files covered:
21-
(100%) none changed - no changed runtime JS files
21+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
22+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - executed lines 33/33; executed functions 7/8
23+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
24+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
2225

2326
Files with executed line/function counts where available:
2427
(2%) src/engine/input/ActionInputService.js - executed lines 397/397; executed functions 1/51
@@ -85,6 +88,7 @@ Files with executed line/function counts where available:
8588
(50%) tools/common/PaletteUsageService.js - executed lines 5/5; executed functions 1/2
8689
(50%) tools/palette-manager-v2/main.js - executed lines 88/88; executed functions 3/6
8790
(50%) tools/palette-manager-v2/modules/PaletteHistoryStack.js - executed lines 54/54; executed functions 5/10
91+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
8892
(50%) tools/toolRegistry.js - executed lines 425/425; executed functions 4/8
8993
(55%) tools/palette-manager-v2/modules/SwatchRow.js - executed lines 115/115; executed functions 6/11
9094
(56%) src/engine/logging/Logger.js - executed lines 56/56; executed functions 5/9
@@ -97,7 +101,6 @@ Files with executed line/function counts where available:
97101
(65%) tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js - executed lines 121/121; executed functions 15/23
98102
(67%) src/shared/number/numberUtils.js - executed lines 14/14; executed functions 2/3
99103
(67%) tools/preview-generator-v2/PreviewGeneratorV2ShellControl.js - executed lines 117/117; executed functions 8/12
100-
(67%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 69/69; executed functions 8/12
101104
(71%) tools/palette-manager-v2/controls/PaletteValidationErrorControl.js - executed lines 30/30; executed functions 5/7
102105
(73%) src/engine/theme/mount-shared-header.js - executed lines 143/143; executed functions 8/11
103106
(75%) src/engine/core/FixedTicker.js - executed lines 36/36; executed functions 3/4
@@ -146,15 +149,20 @@ Files with executed line/function counts where available:
146149
(100%) tools/preview-generator-v2/PreviewGeneratorV2Logger.js - executed lines 19/19; executed functions 5/5
147150
(100%) tools/preview-generator-v2/PreviewGeneratorV2RepoAccess.js - executed lines 21/21; executed functions 5/5
148151
(100%) tools/preview-generator-v2/PreviewGeneratorV2Ui.js - executed lines 48/48; executed functions 9/9
149-
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 42/42; executed functions 4/4
152+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
150153
(100%) tools/templates/first-class-tool-starter/js/controls/AccordionSection.js - executed lines 27/27; executed functions 5/5
151-
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 16/16; executed functions 4/4
154+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
152155
(100%) tools/templates/first-class-tool-starter/js/controls/InspectorControl.js - executed lines 8/8; executed functions 3/3
153156
(100%) tools/templates/first-class-tool-starter/js/controls/PreviewPanelControl.js - executed lines 23/23; executed functions 5/5
154157
(100%) tools/templates/first-class-tool-starter/js/services/ToolStateSerializer.js - executed lines 13/13; executed functions 3/3
155158

156159
Uncovered or low-coverage changed JS files:
157-
(100%) none changed - no changed runtime JS files
160+
(100%) none - no low-coverage changed runtime JS files
158161

159162
Changed JS files considered:
160-
(100%) none - no changed JS files
163+
(0%) tests/playwright/PreviewGeneratorV2Baseline.spec.mjs - changed JS file not collected as browser runtime coverage
164+
(0%) tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs - changed JS file not collected as browser runtime coverage
165+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - changed JS file with browser V8 coverage
166+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - changed JS file with browser V8 coverage
167+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - changed JS file with browser V8 coverage
168+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - changed JS file with browser V8 coverage
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
PR_26126_068-tool-template-launch-mode-nav
2+
3+
Scope:
4+
- Updated the official first-class tool starter at tools/templates/first-class-tool-starter/.
5+
- Updated existing Playwright coverage for the starter launch-mode NAV behavior.
6+
- No live tools, samples, schemas, roadmap, start_of_day folders, or tools/shared files were modified.
7+
8+
Changes:
9+
- Added separate tool-mode and workspace-mode NAV sections.
10+
- Default launch mode is tool mode.
11+
- ?launch=workspace switches visibility to the workspace NAV.
12+
- Tool NAV uses class tool-starter__tool__menu, aria-label Tool actions, and buttons:
13+
- Export
14+
- Copy JSON
15+
- Export toolState
16+
- Workspace NAV uses class tool-starter__workspace__menu, aria-label Workspace actions, and buttons:
17+
- Import manifest
18+
- Copy manifest
19+
- Export manifest
20+
- Added hidden-state CSS so only one NAV displays at a time.
21+
- Kept Preview Generator V2 color-match styling and src/engine/theme usage.
22+
- Preserved no tools/shared runtime dependency.
23+
- Preserved external CSS/JS only; no inline style block, inline script block, or inline event handlers were added.
24+
25+
Validation:
26+
- node --check tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js
27+
- node --check tools/templates/first-class-tool-starter/js/ToolStarterApp.js
28+
- node --check tools/templates/first-class-tool-starter/js/bootstrap.js
29+
- node --check tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js
30+
- node --check tests/playwright/PreviewGeneratorV2Baseline.spec.mjs
31+
- node --check tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs
32+
- rg check confirmed tools/templates/first-class-tool-starter/index.html has no inline style block, inline script block, or inline event handlers.
33+
- git diff --check
34+
- npm run test:workspace-v2
35+
- npx playwright test --config tools/templates/first-class-tool-starter/playwright.config.mjs --reporter=list
36+
37+
Playwright impacted: Yes
38+
- This PR changes template UI controls/interactions and launch-mode state.
39+
- npm run test:workspace-v2 passed.
40+
- Template-local Playwright passed.
41+
42+
Playwright behavior validated:
43+
- Official starter entry point defaults to tool NAV.
44+
- Official starter entry point with ?launch=workspace shows workspace NAV.
45+
- Only one launch-mode NAV is visible for each launch mode.
46+
- Tool and workspace NAV labels and button text match the required casing.
47+
- Duplicate button IDs are not present.
48+
- Existing starter accordion, required-field gating, primary action, and status clear behaviors still work.
49+
50+
Manual test notes:
51+
- Open tools/templates/first-class-tool-starter/index.html and confirm only Tool actions is visible with Export, Copy JSON, and Export toolState.
52+
- Open tools/templates/first-class-tool-starter/index.html?launch=workspace and confirm only Workspace actions is visible with Import manifest, Copy manifest, and Export manifest.
53+
- The official starter path is tools/templates/first-class-tool-starter/index.html; tools/templates/README.md designates that folder as the starter entry point.
54+
55+
Full samples smoke test:
56+
- Skipped. This PR only changes the first-class tool starter template and its targeted tests.

‎tests/playwright/PreviewGeneratorV2Baseline.spec.mjs‎

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ async function openPaletteManager(page) {
120120
returnserver;
121121
}
122122

123-
asyncfunctionopenToolTemplate(page){
123+
asyncfunctionopenToolTemplate(page,query=""){
124124
constserver=awaitstartRepoServer();
125125
awaitcoverageReporter.start(page);
126-
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html`,{waitUntil: "networkidle"});
126+
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html${query}`,{waitUntil: "networkidle"});
127127
returnserver;
128128
}
129129

@@ -466,6 +466,18 @@ test.describe("Preview Generator V2 baseline", () => {
466466
awaitexpect(page.locator("#shared-theme-header")).toBeAttached();
467467
awaitexpect(page.locator("[data-tool-starter-header]")).toContainText("First-Class Tool Starter");
468468
awaitexpect(page.locator("[data-tool-starter-summary]")).toHaveAttribute("data-tools-platform-summary-active","1");
469+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeVisible();
470+
awaitexpect(page.locator(".tool-starter__tool__menu")).toHaveAttribute("aria-label","Tool actions");
471+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeHidden();
472+
awaitexpect(page.locator("#toolExportButton")).toHaveText("Export");
473+
awaitexpect(page.locator("#toolCopyJsonButton")).toHaveText("Copy JSON");
474+
awaitexpect(page.locator("#toolExportToolStateButton")).toHaveText("Export toolState");
475+
476+
constduplicateIds=awaitpage.evaluate(()=>{
477+
constids=[...document.querySelectorAll("[id]")].map((element)=>element.id);
478+
returnids.filter((id,index)=>ids.indexOf(id)!==index);
479+
});
480+
expect(duplicateIds).toEqual([]);
469481

470482
constsharedReferences=awaitpage.evaluate(()=>[
471483
...document.querySelectorAll("script[src],link[href]")
@@ -482,17 +494,23 @@ test.describe("Preview Generator V2 baseline", () => {
482494

483495
awaitexpectAccordionToggles(page,"sourceInputContent");
484496

485-
construnButton=page.locator("#runToolButton");
486-
awaitexpect(runButton).toBeDisabled();
497+
constexportButton=page.locator("#toolExportButton");
498+
constcopyJsonButton=page.locator("#toolCopyJsonButton");
499+
constexportToolStateButton=page.locator("#toolExportToolStateButton");
500+
awaitexpect(exportButton).toBeDisabled();
501+
awaitexpect(copyJsonButton).toBeDisabled();
502+
awaitexpect(exportToolStateButton).toBeDisabled();
487503
awaitpage.locator("#sourceInput").fill("starter value");
488-
awaitexpect(runButton).toBeEnabled();
489-
awaitrunButton.click();
504+
awaitexpect(exportButton).toBeEnabled();
505+
awaitexpect(copyJsonButton).toBeEnabled();
506+
awaitexpect(exportToolStateButton).toBeEnabled();
507+
awaitexportButton.click();
490508
awaitexpect(page.locator("#statusLog")).toHaveValue(/Processedsourcevalue/);
491509
awaitpage.locator("#clearStatusButton").click();
492510
awaitexpect(page.locator("#statusLog")).toHaveValue("");
493511

494512
awaitpage.locator("#sourceInput").fill("");
495-
awaitexpect(runButton).toBeDisabled();
513+
awaitexpect(exportButton).toBeDisabled();
496514
awaitexpect(page.locator("#sourceValidationMessage")).toContainText("Input is required");
497515

498516
expect(pageErrors).toEqual([]);
@@ -501,4 +519,26 @@ test.describe("Preview Generator V2 baseline", () => {
501519
awaitserver.close();
502520
}
503521
});
522+
523+
test("launches first-class tool starter template in workspace nav mode",async({ page })=>{
524+
constserver=awaitopenToolTemplate(page,"?launch=workspace");
525+
constpageErrors=[];
526+
527+
page.on("pageerror",(error)=>{
528+
pageErrors.push(error.message);
529+
});
530+
531+
try{
532+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeHidden();
533+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeVisible();
534+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toHaveAttribute("aria-label","Workspace actions");
535+
awaitexpect(page.locator("#workspaceImportManifestButton")).toHaveText("Import manifest");
536+
awaitexpect(page.locator("#workspaceCopyManifestButton")).toHaveText("Copy manifest");
537+
awaitexpect(page.locator("#workspaceExportManifestButton")).toHaveText("Export manifest");
538+
expect(pageErrors).toEqual([]);
539+
}finally{
540+
awaitcoverageReporter.stop(page);
541+
awaitserver.close();
542+
}
543+
});
504544
});

‎tools/templates/first-class-tool-starter/index.html‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,16 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
3636
</div>
3737
</details>
3838

39-
<navclass="tool-starter__menu" aria-label="Tool actions">
40-
<buttonid="runToolButton" type="button">Run</button>
41-
<buttonid="resetToolButton" type="button">Reset</button>
42-
<buttonid="exportToolStateButton" type="button">Export toolState</button>
39+
<navclass="tool-starter__menu tool-starter__tool__menu" aria-label="Tool actions" data-launch-mode-nav="tool">
40+
<buttonid="toolExportButton" type="button">Export</button>
41+
<buttonid="toolCopyJsonButton" type="button">Copy JSON</button>
42+
<buttonid="toolExportToolStateButton" type="button">Export toolState</button>
43+
</nav>
44+
45+
<navclass="tool-starter__menu tool-starter__workspace__menu" aria-label="Workspace actions" data-launch-mode-nav="workspace" hidden>
46+
<buttonid="workspaceImportManifestButton" type="button">Import manifest</button>
47+
<buttonid="workspaceCopyManifestButton" type="button">Copy manifest</button>
48+
<buttonid="workspaceExportManifestButton" type="button">Export manifest</button>
4349
</nav>
4450

4551
<mainclass="tool-starter app-shell" data-tool-id="first-class-tool-starter">
@@ -54,7 +60,7 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
5460
<span>Source value</span>
5561
<inputid="sourceInput" type="text" autocomplete="off" placeholder="Enter a value to process">
5662
</label>
57-
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Run can process.</p>
63+
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Export can process.</p>
5864
</div>
5965
</section>
6066

‎tools/templates/first-class-tool-starter/js/ToolStarterApp.js‎

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
exportclassToolStarterApp{
2-
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog }){
2+
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog, windowRef =window}){
33
this.accordions=accordions;
44
this.actionNav=actionNav;
55
this.inspector=inspector;
@@ -8,15 +8,21 @@ export class ToolStarterApp {
88
this.shell=shell;
99
this.sourceInput=sourceInput;
1010
this.statusLog=statusLog;
11+
this.window=windowRef;
1112
}
1213

1314
start(){
1415
this.shell.mount();
1516
this.accordions.forEach((accordion)=>accordion.mount());
1617
this.actionNav.mount({
17-
onExport: ()=>this.exportToolState(),
18-
onReset: ()=>this.reset(),
19-
onRun: ()=>this.run()
18+
onToolCopyJson: ()=>{
19+
voidthis.copyJson();
20+
},
21+
onToolExport: ()=>this.run(),
22+
onToolExportToolState: ()=>this.exportToolState(),
23+
onWorkspaceCopyManifest: ()=>this.statusLog.write("Copy manifest action ready for workspace wiring."),
24+
onWorkspaceExportManifest: ()=>this.statusLog.write("Export manifest action ready for workspace wiring."),
25+
onWorkspaceImportManifest: ()=>this.statusLog.write("Import manifest action ready for workspace wiring.")
2026
});
2127
this.sourceInput.mount({
2228
onChange: ()=>this.refreshActions()
@@ -66,11 +72,37 @@ export class ToolStarterApp {
6672
this.refreshActions();
6773
}
6874

75+
asynccopyJson(){
76+
constvalidation=this.sourceInput.validate();
77+
if(!validation.valid){
78+
this.statusLog.error(validation.message);
79+
this.refreshActions();
80+
return;
81+
}
82+
83+
consttoolState=this.serializer.createToolState({sourceValue: validation.value});
84+
this.inspector.showObject(toolState);
85+
constjson=JSON.stringify(toolState,null,2);
86+
if(typeofthis.window.navigator?.clipboard?.writeText!=="function"){
87+
this.statusLog.write("toolState JSON preview written to Output Summary. Clipboard API is unavailable.");
88+
this.refreshActions();
89+
return;
90+
}
91+
92+
try{
93+
awaitthis.window.navigator.clipboard.writeText(json);
94+
this.statusLog.write("toolState JSON copied.");
95+
}catch(error){
96+
this.statusLog.error(`Copy JSON failed: ${error.message}`);
97+
}
98+
this.refreshActions();
99+
}
100+
69101
refreshActions(){
70102
consthasValue=this.sourceInput.hasValue();
71103
if(!hasValue){
72-
this.sourceInput.showMessage("Input is required before Run can process.",false);
104+
this.sourceInput.showMessage("Input is required before Export can process.",false);
73105
}
74-
this.actionNav.setRunEnabled(hasValue);
106+
this.actionNav.setToolActionsEnabled(hasValue);
75107
}
76108
}

‎tools/templates/first-class-tool-starter/js/bootstrap.js‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,22 @@ window.addEventListener("DOMContentLoaded", () => {
2929
constapp=newToolStarterApp({
3030
accordions,
3131
actionNav: newActionNavControl({
32-
runButton: requireElement("#runToolButton"),
33-
resetButton: requireElement("#resetToolButton"),
34-
exportButton: requireElement("#exportToolStateButton")
32+
toolCopyJsonButton: requireElement("#toolCopyJsonButton"),
33+
toolExportButton: requireElement("#toolExportButton"),
34+
toolExportToolStateButton: requireElement("#toolExportToolStateButton"),
35+
toolNav: requireElement(".tool-starter__tool__menu"),
36+
workspaceCopyManifestButton: requireElement("#workspaceCopyManifestButton"),
37+
workspaceExportManifestButton: requireElement("#workspaceExportManifestButton"),
38+
workspaceImportManifestButton: requireElement("#workspaceImportManifestButton"),
39+
workspaceNav: requireElement(".tool-starter__workspace__menu")
3540
}),
3641
inspector: newInspectorControl(requireElement("#inspectorOutput")),
3742
preview: newPreviewPanelControl(requireElement("#previewOutput")),
3843
serializer: newToolStateSerializer("first-class-tool-starter"),
3944
shell: newToolStarterShellControl(),
4045
sourceInput,
41-
statusLog
46+
statusLog,
47+
windowRef: window
4248
});
4349

4450
app.start();

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add launch-mode-specific NAV behavior to first-class tool template - … · ToolboxAid/HTML-JavaScript-Gaming@ed61e63 · GitHub
Skip to content

Commit ed61e63

Browse files
committed
Add launch-mode-specific NAV behavior to first-class tool template - PR_26126_068-tool-template-launch-mode-nav
1 parent 0f067a9 commit ed61e63

10 files changed

Lines changed: 265 additions & 50 deletions

File tree

‎docs/dev/reports/playwright_v8_coverage_report.txt‎

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ Note: coverage entries are aggregated across every page/tool where coverageRepor
1313
Exercised tool entry points detected:
1414
(84%) Preview Generator V2 - exercised 19 runtime JS files
1515
(49%) Palette Manager - exercised 12 runtime JS files
16-
(89%) First-Class Tool Starter Template - exercised 10 runtime JS files
16+
(84%) First-Class Tool Starter Template - exercised 10 runtime JS files
1717
(0%) Workspace V2 - not exercised by this Playwright run
1818
(0%) Workspace Manager - not exercised by this Playwright run
1919

2020
Changed runtime JS files covered:
21-
(100%) none changed - no changed runtime JS files
21+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
22+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - executed lines 33/33; executed functions 7/8
23+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
24+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
2225

2326
Files with executed line/function counts where available:
2427
(2%) src/engine/input/ActionInputService.js - executed lines 397/397; executed functions 1/51
@@ -85,6 +88,7 @@ Files with executed line/function counts where available:
8588
(50%) tools/common/PaletteUsageService.js - executed lines 5/5; executed functions 1/2
8689
(50%) tools/palette-manager-v2/main.js - executed lines 88/88; executed functions 3/6
8790
(50%) tools/palette-manager-v2/modules/PaletteHistoryStack.js - executed lines 54/54; executed functions 5/10
91+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
8892
(50%) tools/toolRegistry.js - executed lines 425/425; executed functions 4/8
8993
(55%) tools/palette-manager-v2/modules/SwatchRow.js - executed lines 115/115; executed functions 6/11
9094
(56%) src/engine/logging/Logger.js - executed lines 56/56; executed functions 5/9
@@ -97,7 +101,6 @@ Files with executed line/function counts where available:
97101
(65%) tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js - executed lines 121/121; executed functions 15/23
98102
(67%) src/shared/number/numberUtils.js - executed lines 14/14; executed functions 2/3
99103
(67%) tools/preview-generator-v2/PreviewGeneratorV2ShellControl.js - executed lines 117/117; executed functions 8/12
100-
(67%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 69/69; executed functions 8/12
101104
(71%) tools/palette-manager-v2/controls/PaletteValidationErrorControl.js - executed lines 30/30; executed functions 5/7
102105
(73%) src/engine/theme/mount-shared-header.js - executed lines 143/143; executed functions 8/11
103106
(75%) src/engine/core/FixedTicker.js - executed lines 36/36; executed functions 3/4
@@ -146,15 +149,20 @@ Files with executed line/function counts where available:
146149
(100%) tools/preview-generator-v2/PreviewGeneratorV2Logger.js - executed lines 19/19; executed functions 5/5
147150
(100%) tools/preview-generator-v2/PreviewGeneratorV2RepoAccess.js - executed lines 21/21; executed functions 5/5
148151
(100%) tools/preview-generator-v2/PreviewGeneratorV2Ui.js - executed lines 48/48; executed functions 9/9
149-
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 42/42; executed functions 4/4
152+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
150153
(100%) tools/templates/first-class-tool-starter/js/controls/AccordionSection.js - executed lines 27/27; executed functions 5/5
151-
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 16/16; executed functions 4/4
154+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
152155
(100%) tools/templates/first-class-tool-starter/js/controls/InspectorControl.js - executed lines 8/8; executed functions 3/3
153156
(100%) tools/templates/first-class-tool-starter/js/controls/PreviewPanelControl.js - executed lines 23/23; executed functions 5/5
154157
(100%) tools/templates/first-class-tool-starter/js/services/ToolStateSerializer.js - executed lines 13/13; executed functions 3/3
155158

156159
Uncovered or low-coverage changed JS files:
157-
(100%) none changed - no changed runtime JS files
160+
(100%) none - no low-coverage changed runtime JS files
158161

159162
Changed JS files considered:
160-
(100%) none - no changed JS files
163+
(0%) tests/playwright/PreviewGeneratorV2Baseline.spec.mjs - changed JS file not collected as browser runtime coverage
164+
(0%) tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs - changed JS file not collected as browser runtime coverage
165+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - changed JS file with browser V8 coverage
166+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - changed JS file with browser V8 coverage
167+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - changed JS file with browser V8 coverage
168+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - changed JS file with browser V8 coverage
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
PR_26126_068-tool-template-launch-mode-nav
2+
3+
Scope:
4+
- Updated the official first-class tool starter at tools/templates/first-class-tool-starter/.
5+
- Updated existing Playwright coverage for the starter launch-mode NAV behavior.
6+
- No live tools, samples, schemas, roadmap, start_of_day folders, or tools/shared files were modified.
7+
8+
Changes:
9+
- Added separate tool-mode and workspace-mode NAV sections.
10+
- Default launch mode is tool mode.
11+
- ?launch=workspace switches visibility to the workspace NAV.
12+
- Tool NAV uses class tool-starter__tool__menu, aria-label Tool actions, and buttons:
13+
- Export
14+
- Copy JSON
15+
- Export toolState
16+
- Workspace NAV uses class tool-starter__workspace__menu, aria-label Workspace actions, and buttons:
17+
- Import manifest
18+
- Copy manifest
19+
- Export manifest
20+
- Added hidden-state CSS so only one NAV displays at a time.
21+
- Kept Preview Generator V2 color-match styling and src/engine/theme usage.
22+
- Preserved no tools/shared runtime dependency.
23+
- Preserved external CSS/JS only; no inline style block, inline script block, or inline event handlers were added.
24+
25+
Validation:
26+
- node --check tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js
27+
- node --check tools/templates/first-class-tool-starter/js/ToolStarterApp.js
28+
- node --check tools/templates/first-class-tool-starter/js/bootstrap.js
29+
- node --check tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js
30+
- node --check tests/playwright/PreviewGeneratorV2Baseline.spec.mjs
31+
- node --check tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs
32+
- rg check confirmed tools/templates/first-class-tool-starter/index.html has no inline style block, inline script block, or inline event handlers.
33+
- git diff --check
34+
- npm run test:workspace-v2
35+
- npx playwright test --config tools/templates/first-class-tool-starter/playwright.config.mjs --reporter=list
36+
37+
Playwright impacted: Yes
38+
- This PR changes template UI controls/interactions and launch-mode state.
39+
- npm run test:workspace-v2 passed.
40+
- Template-local Playwright passed.
41+
42+
Playwright behavior validated:
43+
- Official starter entry point defaults to tool NAV.
44+
- Official starter entry point with ?launch=workspace shows workspace NAV.
45+
- Only one launch-mode NAV is visible for each launch mode.
46+
- Tool and workspace NAV labels and button text match the required casing.
47+
- Duplicate button IDs are not present.
48+
- Existing starter accordion, required-field gating, primary action, and status clear behaviors still work.
49+
50+
Manual test notes:
51+
- Open tools/templates/first-class-tool-starter/index.html and confirm only Tool actions is visible with Export, Copy JSON, and Export toolState.
52+
- Open tools/templates/first-class-tool-starter/index.html?launch=workspace and confirm only Workspace actions is visible with Import manifest, Copy manifest, and Export manifest.
53+
- The official starter path is tools/templates/first-class-tool-starter/index.html; tools/templates/README.md designates that folder as the starter entry point.
54+
55+
Full samples smoke test:
56+
- Skipped. This PR only changes the first-class tool starter template and its targeted tests.

‎tests/playwright/PreviewGeneratorV2Baseline.spec.mjs‎

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ async function openPaletteManager(page) {
120120
returnserver;
121121
}
122122

123-
asyncfunctionopenToolTemplate(page){
123+
asyncfunctionopenToolTemplate(page,query=""){
124124
constserver=awaitstartRepoServer();
125125
awaitcoverageReporter.start(page);
126-
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html`,{waitUntil: "networkidle"});
126+
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html${query}`,{waitUntil: "networkidle"});
127127
returnserver;
128128
}
129129

@@ -466,6 +466,18 @@ test.describe("Preview Generator V2 baseline", () => {
466466
awaitexpect(page.locator("#shared-theme-header")).toBeAttached();
467467
awaitexpect(page.locator("[data-tool-starter-header]")).toContainText("First-Class Tool Starter");
468468
awaitexpect(page.locator("[data-tool-starter-summary]")).toHaveAttribute("data-tools-platform-summary-active","1");
469+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeVisible();
470+
awaitexpect(page.locator(".tool-starter__tool__menu")).toHaveAttribute("aria-label","Tool actions");
471+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeHidden();
472+
awaitexpect(page.locator("#toolExportButton")).toHaveText("Export");
473+
awaitexpect(page.locator("#toolCopyJsonButton")).toHaveText("Copy JSON");
474+
awaitexpect(page.locator("#toolExportToolStateButton")).toHaveText("Export toolState");
475+
476+
constduplicateIds=awaitpage.evaluate(()=>{
477+
constids=[...document.querySelectorAll("[id]")].map((element)=>element.id);
478+
returnids.filter((id,index)=>ids.indexOf(id)!==index);
479+
});
480+
expect(duplicateIds).toEqual([]);
469481

470482
constsharedReferences=awaitpage.evaluate(()=>[
471483
...document.querySelectorAll("script[src],link[href]")
@@ -482,17 +494,23 @@ test.describe("Preview Generator V2 baseline", () => {
482494

483495
awaitexpectAccordionToggles(page,"sourceInputContent");
484496

485-
construnButton=page.locator("#runToolButton");
486-
awaitexpect(runButton).toBeDisabled();
497+
constexportButton=page.locator("#toolExportButton");
498+
constcopyJsonButton=page.locator("#toolCopyJsonButton");
499+
constexportToolStateButton=page.locator("#toolExportToolStateButton");
500+
awaitexpect(exportButton).toBeDisabled();
501+
awaitexpect(copyJsonButton).toBeDisabled();
502+
awaitexpect(exportToolStateButton).toBeDisabled();
487503
awaitpage.locator("#sourceInput").fill("starter value");
488-
awaitexpect(runButton).toBeEnabled();
489-
awaitrunButton.click();
504+
awaitexpect(exportButton).toBeEnabled();
505+
awaitexpect(copyJsonButton).toBeEnabled();
506+
awaitexpect(exportToolStateButton).toBeEnabled();
507+
awaitexportButton.click();
490508
awaitexpect(page.locator("#statusLog")).toHaveValue(/Processedsourcevalue/);
491509
awaitpage.locator("#clearStatusButton").click();
492510
awaitexpect(page.locator("#statusLog")).toHaveValue("");
493511

494512
awaitpage.locator("#sourceInput").fill("");
495-
awaitexpect(runButton).toBeDisabled();
513+
awaitexpect(exportButton).toBeDisabled();
496514
awaitexpect(page.locator("#sourceValidationMessage")).toContainText("Input is required");
497515

498516
expect(pageErrors).toEqual([]);
@@ -501,4 +519,26 @@ test.describe("Preview Generator V2 baseline", () => {
501519
awaitserver.close();
502520
}
503521
});
522+
523+
test("launches first-class tool starter template in workspace nav mode",async({ page })=>{
524+
constserver=awaitopenToolTemplate(page,"?launch=workspace");
525+
constpageErrors=[];
526+
527+
page.on("pageerror",(error)=>{
528+
pageErrors.push(error.message);
529+
});
530+
531+
try{
532+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeHidden();
533+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeVisible();
534+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toHaveAttribute("aria-label","Workspace actions");
535+
awaitexpect(page.locator("#workspaceImportManifestButton")).toHaveText("Import manifest");
536+
awaitexpect(page.locator("#workspaceCopyManifestButton")).toHaveText("Copy manifest");
537+
awaitexpect(page.locator("#workspaceExportManifestButton")).toHaveText("Export manifest");
538+
expect(pageErrors).toEqual([]);
539+
}finally{
540+
awaitcoverageReporter.stop(page);
541+
awaitserver.close();
542+
}
543+
});
504544
});

‎tools/templates/first-class-tool-starter/index.html‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,16 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
3636
</div>
3737
</details>
3838

39-
<navclass="tool-starter__menu" aria-label="Tool actions">
40-
<buttonid="runToolButton" type="button">Run</button>
41-
<buttonid="resetToolButton" type="button">Reset</button>
42-
<buttonid="exportToolStateButton" type="button">Export toolState</button>
39+
<navclass="tool-starter__menu tool-starter__tool__menu" aria-label="Tool actions" data-launch-mode-nav="tool">
40+
<buttonid="toolExportButton" type="button">Export</button>
41+
<buttonid="toolCopyJsonButton" type="button">Copy JSON</button>
42+
<buttonid="toolExportToolStateButton" type="button">Export toolState</button>
43+
</nav>
44+
45+
<navclass="tool-starter__menu tool-starter__workspace__menu" aria-label="Workspace actions" data-launch-mode-nav="workspace" hidden>
46+
<buttonid="workspaceImportManifestButton" type="button">Import manifest</button>
47+
<buttonid="workspaceCopyManifestButton" type="button">Copy manifest</button>
48+
<buttonid="workspaceExportManifestButton" type="button">Export manifest</button>
4349
</nav>
4450

4551
<mainclass="tool-starter app-shell" data-tool-id="first-class-tool-starter">
@@ -54,7 +60,7 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
5460
<span>Source value</span>
5561
<inputid="sourceInput" type="text" autocomplete="off" placeholder="Enter a value to process">
5662
</label>
57-
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Run can process.</p>
63+
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Export can process.</p>
5864
</div>
5965
</section>
6066

‎tools/templates/first-class-tool-starter/js/ToolStarterApp.js‎

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
exportclassToolStarterApp{
2-
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog }){
2+
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog, windowRef =window}){
33
this.accordions=accordions;
44
this.actionNav=actionNav;
55
this.inspector=inspector;
@@ -8,15 +8,21 @@ export class ToolStarterApp {
88
this.shell=shell;
99
this.sourceInput=sourceInput;
1010
this.statusLog=statusLog;
11+
this.window=windowRef;
1112
}
1213

1314
start(){
1415
this.shell.mount();
1516
this.accordions.forEach((accordion)=>accordion.mount());
1617
this.actionNav.mount({
17-
onExport: ()=>this.exportToolState(),
18-
onReset: ()=>this.reset(),
19-
onRun: ()=>this.run()
18+
onToolCopyJson: ()=>{
19+
voidthis.copyJson();
20+
},
21+
onToolExport: ()=>this.run(),
22+
onToolExportToolState: ()=>this.exportToolState(),
23+
onWorkspaceCopyManifest: ()=>this.statusLog.write("Copy manifest action ready for workspace wiring."),
24+
onWorkspaceExportManifest: ()=>this.statusLog.write("Export manifest action ready for workspace wiring."),
25+
onWorkspaceImportManifest: ()=>this.statusLog.write("Import manifest action ready for workspace wiring.")
2026
});
2127
this.sourceInput.mount({
2228
onChange: ()=>this.refreshActions()
@@ -66,11 +72,37 @@ export class ToolStarterApp {
6672
this.refreshActions();
6773
}
6874

75+
asynccopyJson(){
76+
constvalidation=this.sourceInput.validate();
77+
if(!validation.valid){
78+
this.statusLog.error(validation.message);
79+
this.refreshActions();
80+
return;
81+
}
82+
83+
consttoolState=this.serializer.createToolState({sourceValue: validation.value});
84+
this.inspector.showObject(toolState);
85+
constjson=JSON.stringify(toolState,null,2);
86+
if(typeofthis.window.navigator?.clipboard?.writeText!=="function"){
87+
this.statusLog.write("toolState JSON preview written to Output Summary. Clipboard API is unavailable.");
88+
this.refreshActions();
89+
return;
90+
}
91+
92+
try{
93+
awaitthis.window.navigator.clipboard.writeText(json);
94+
this.statusLog.write("toolState JSON copied.");
95+
}catch(error){
96+
this.statusLog.error(`Copy JSON failed: ${error.message}`);
97+
}
98+
this.refreshActions();
99+
}
100+
69101
refreshActions(){
70102
consthasValue=this.sourceInput.hasValue();
71103
if(!hasValue){
72-
this.sourceInput.showMessage("Input is required before Run can process.",false);
104+
this.sourceInput.showMessage("Input is required before Export can process.",false);
73105
}
74-
this.actionNav.setRunEnabled(hasValue);
106+
this.actionNav.setToolActionsEnabled(hasValue);
75107
}
76108
}

‎tools/templates/first-class-tool-starter/js/bootstrap.js‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,22 @@ window.addEventListener("DOMContentLoaded", () => {
2929
constapp=newToolStarterApp({
3030
accordions,
3131
actionNav: newActionNavControl({
32-
runButton: requireElement("#runToolButton"),
33-
resetButton: requireElement("#resetToolButton"),
34-
exportButton: requireElement("#exportToolStateButton")
32+
toolCopyJsonButton: requireElement("#toolCopyJsonButton"),
33+
toolExportButton: requireElement("#toolExportButton"),
34+
toolExportToolStateButton: requireElement("#toolExportToolStateButton"),
35+
toolNav: requireElement(".tool-starter__tool__menu"),
36+
workspaceCopyManifestButton: requireElement("#workspaceCopyManifestButton"),
37+
workspaceExportManifestButton: requireElement("#workspaceExportManifestButton"),
38+
workspaceImportManifestButton: requireElement("#workspaceImportManifestButton"),
39+
workspaceNav: requireElement(".tool-starter__workspace__menu")
3540
}),
3641
inspector: newInspectorControl(requireElement("#inspectorOutput")),
3742
preview: newPreviewPanelControl(requireElement("#previewOutput")),
3843
serializer: newToolStateSerializer("first-class-tool-starter"),
3944
shell: newToolStarterShellControl(),
4045
sourceInput,
41-
statusLog
46+
statusLog,
47+
windowRef: window
4248
});
4349

4450
app.start();

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Add launch-mode-specific NAV behavior to first-class tool template - … · ToolboxAid/HTML-JavaScript-Gaming@ed61e63 · GitHub
Skip to content

Commit ed61e63

Browse files
committed
Add launch-mode-specific NAV behavior to first-class tool template - PR_26126_068-tool-template-launch-mode-nav
1 parent 0f067a9 commit ed61e63

10 files changed

Lines changed: 265 additions & 50 deletions

File tree

‎docs/dev/reports/playwright_v8_coverage_report.txt‎

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ Note: coverage entries are aggregated across every page/tool where coverageRepor
1313
Exercised tool entry points detected:
1414
(84%) Preview Generator V2 - exercised 19 runtime JS files
1515
(49%) Palette Manager - exercised 12 runtime JS files
16-
(89%) First-Class Tool Starter Template - exercised 10 runtime JS files
16+
(84%) First-Class Tool Starter Template - exercised 10 runtime JS files
1717
(0%) Workspace V2 - not exercised by this Playwright run
1818
(0%) Workspace Manager - not exercised by this Playwright run
1919

2020
Changed runtime JS files covered:
21-
(100%) none changed - no changed runtime JS files
21+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
22+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - executed lines 33/33; executed functions 7/8
23+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
24+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
2225

2326
Files with executed line/function counts where available:
2427
(2%) src/engine/input/ActionInputService.js - executed lines 397/397; executed functions 1/51
@@ -85,6 +88,7 @@ Files with executed line/function counts where available:
8588
(50%) tools/common/PaletteUsageService.js - executed lines 5/5; executed functions 1/2
8689
(50%) tools/palette-manager-v2/main.js - executed lines 88/88; executed functions 3/6
8790
(50%) tools/palette-manager-v2/modules/PaletteHistoryStack.js - executed lines 54/54; executed functions 5/10
91+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
8892
(50%) tools/toolRegistry.js - executed lines 425/425; executed functions 4/8
8993
(55%) tools/palette-manager-v2/modules/SwatchRow.js - executed lines 115/115; executed functions 6/11
9094
(56%) src/engine/logging/Logger.js - executed lines 56/56; executed functions 5/9
@@ -97,7 +101,6 @@ Files with executed line/function counts where available:
97101
(65%) tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js - executed lines 121/121; executed functions 15/23
98102
(67%) src/shared/number/numberUtils.js - executed lines 14/14; executed functions 2/3
99103
(67%) tools/preview-generator-v2/PreviewGeneratorV2ShellControl.js - executed lines 117/117; executed functions 8/12
100-
(67%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 69/69; executed functions 8/12
101104
(71%) tools/palette-manager-v2/controls/PaletteValidationErrorControl.js - executed lines 30/30; executed functions 5/7
102105
(73%) src/engine/theme/mount-shared-header.js - executed lines 143/143; executed functions 8/11
103106
(75%) src/engine/core/FixedTicker.js - executed lines 36/36; executed functions 3/4
@@ -146,15 +149,20 @@ Files with executed line/function counts where available:
146149
(100%) tools/preview-generator-v2/PreviewGeneratorV2Logger.js - executed lines 19/19; executed functions 5/5
147150
(100%) tools/preview-generator-v2/PreviewGeneratorV2RepoAccess.js - executed lines 21/21; executed functions 5/5
148151
(100%) tools/preview-generator-v2/PreviewGeneratorV2Ui.js - executed lines 48/48; executed functions 9/9
149-
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 42/42; executed functions 4/4
152+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
150153
(100%) tools/templates/first-class-tool-starter/js/controls/AccordionSection.js - executed lines 27/27; executed functions 5/5
151-
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 16/16; executed functions 4/4
154+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
152155
(100%) tools/templates/first-class-tool-starter/js/controls/InspectorControl.js - executed lines 8/8; executed functions 3/3
153156
(100%) tools/templates/first-class-tool-starter/js/controls/PreviewPanelControl.js - executed lines 23/23; executed functions 5/5
154157
(100%) tools/templates/first-class-tool-starter/js/services/ToolStateSerializer.js - executed lines 13/13; executed functions 3/3
155158

156159
Uncovered or low-coverage changed JS files:
157-
(100%) none changed - no changed runtime JS files
160+
(100%) none - no low-coverage changed runtime JS files
158161

159162
Changed JS files considered:
160-
(100%) none - no changed JS files
163+
(0%) tests/playwright/PreviewGeneratorV2Baseline.spec.mjs - changed JS file not collected as browser runtime coverage
164+
(0%) tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs - changed JS file not collected as browser runtime coverage
165+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - changed JS file with browser V8 coverage
166+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - changed JS file with browser V8 coverage
167+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - changed JS file with browser V8 coverage
168+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - changed JS file with browser V8 coverage
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
PR_26126_068-tool-template-launch-mode-nav
2+
3+
Scope:
4+
- Updated the official first-class tool starter at tools/templates/first-class-tool-starter/.
5+
- Updated existing Playwright coverage for the starter launch-mode NAV behavior.
6+
- No live tools, samples, schemas, roadmap, start_of_day folders, or tools/shared files were modified.
7+
8+
Changes:
9+
- Added separate tool-mode and workspace-mode NAV sections.
10+
- Default launch mode is tool mode.
11+
- ?launch=workspace switches visibility to the workspace NAV.
12+
- Tool NAV uses class tool-starter__tool__menu, aria-label Tool actions, and buttons:
13+
- Export
14+
- Copy JSON
15+
- Export toolState
16+
- Workspace NAV uses class tool-starter__workspace__menu, aria-label Workspace actions, and buttons:
17+
- Import manifest
18+
- Copy manifest
19+
- Export manifest
20+
- Added hidden-state CSS so only one NAV displays at a time.
21+
- Kept Preview Generator V2 color-match styling and src/engine/theme usage.
22+
- Preserved no tools/shared runtime dependency.
23+
- Preserved external CSS/JS only; no inline style block, inline script block, or inline event handlers were added.
24+
25+
Validation:
26+
- node --check tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js
27+
- node --check tools/templates/first-class-tool-starter/js/ToolStarterApp.js
28+
- node --check tools/templates/first-class-tool-starter/js/bootstrap.js
29+
- node --check tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js
30+
- node --check tests/playwright/PreviewGeneratorV2Baseline.spec.mjs
31+
- node --check tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs
32+
- rg check confirmed tools/templates/first-class-tool-starter/index.html has no inline style block, inline script block, or inline event handlers.
33+
- git diff --check
34+
- npm run test:workspace-v2
35+
- npx playwright test --config tools/templates/first-class-tool-starter/playwright.config.mjs --reporter=list
36+
37+
Playwright impacted: Yes
38+
- This PR changes template UI controls/interactions and launch-mode state.
39+
- npm run test:workspace-v2 passed.
40+
- Template-local Playwright passed.
41+
42+
Playwright behavior validated:
43+
- Official starter entry point defaults to tool NAV.
44+
- Official starter entry point with ?launch=workspace shows workspace NAV.
45+
- Only one launch-mode NAV is visible for each launch mode.
46+
- Tool and workspace NAV labels and button text match the required casing.
47+
- Duplicate button IDs are not present.
48+
- Existing starter accordion, required-field gating, primary action, and status clear behaviors still work.
49+
50+
Manual test notes:
51+
- Open tools/templates/first-class-tool-starter/index.html and confirm only Tool actions is visible with Export, Copy JSON, and Export toolState.
52+
- Open tools/templates/first-class-tool-starter/index.html?launch=workspace and confirm only Workspace actions is visible with Import manifest, Copy manifest, and Export manifest.
53+
- The official starter path is tools/templates/first-class-tool-starter/index.html; tools/templates/README.md designates that folder as the starter entry point.
54+
55+
Full samples smoke test:
56+
- Skipped. This PR only changes the first-class tool starter template and its targeted tests.

‎tests/playwright/PreviewGeneratorV2Baseline.spec.mjs‎

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ async function openPaletteManager(page) {
120120
returnserver;
121121
}
122122

123-
asyncfunctionopenToolTemplate(page){
123+
asyncfunctionopenToolTemplate(page,query=""){
124124
constserver=awaitstartRepoServer();
125125
awaitcoverageReporter.start(page);
126-
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html`,{waitUntil: "networkidle"});
126+
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html${query}`,{waitUntil: "networkidle"});
127127
returnserver;
128128
}
129129

@@ -466,6 +466,18 @@ test.describe("Preview Generator V2 baseline", () => {
466466
awaitexpect(page.locator("#shared-theme-header")).toBeAttached();
467467
awaitexpect(page.locator("[data-tool-starter-header]")).toContainText("First-Class Tool Starter");
468468
awaitexpect(page.locator("[data-tool-starter-summary]")).toHaveAttribute("data-tools-platform-summary-active","1");
469+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeVisible();
470+
awaitexpect(page.locator(".tool-starter__tool__menu")).toHaveAttribute("aria-label","Tool actions");
471+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeHidden();
472+
awaitexpect(page.locator("#toolExportButton")).toHaveText("Export");
473+
awaitexpect(page.locator("#toolCopyJsonButton")).toHaveText("Copy JSON");
474+
awaitexpect(page.locator("#toolExportToolStateButton")).toHaveText("Export toolState");
475+
476+
constduplicateIds=awaitpage.evaluate(()=>{
477+
constids=[...document.querySelectorAll("[id]")].map((element)=>element.id);
478+
returnids.filter((id,index)=>ids.indexOf(id)!==index);
479+
});
480+
expect(duplicateIds).toEqual([]);
469481

470482
constsharedReferences=awaitpage.evaluate(()=>[
471483
...document.querySelectorAll("script[src],link[href]")
@@ -482,17 +494,23 @@ test.describe("Preview Generator V2 baseline", () => {
482494

483495
awaitexpectAccordionToggles(page,"sourceInputContent");
484496

485-
construnButton=page.locator("#runToolButton");
486-
awaitexpect(runButton).toBeDisabled();
497+
constexportButton=page.locator("#toolExportButton");
498+
constcopyJsonButton=page.locator("#toolCopyJsonButton");
499+
constexportToolStateButton=page.locator("#toolExportToolStateButton");
500+
awaitexpect(exportButton).toBeDisabled();
501+
awaitexpect(copyJsonButton).toBeDisabled();
502+
awaitexpect(exportToolStateButton).toBeDisabled();
487503
awaitpage.locator("#sourceInput").fill("starter value");
488-
awaitexpect(runButton).toBeEnabled();
489-
awaitrunButton.click();
504+
awaitexpect(exportButton).toBeEnabled();
505+
awaitexpect(copyJsonButton).toBeEnabled();
506+
awaitexpect(exportToolStateButton).toBeEnabled();
507+
awaitexportButton.click();
490508
awaitexpect(page.locator("#statusLog")).toHaveValue(/Processedsourcevalue/);
491509
awaitpage.locator("#clearStatusButton").click();
492510
awaitexpect(page.locator("#statusLog")).toHaveValue("");
493511

494512
awaitpage.locator("#sourceInput").fill("");
495-
awaitexpect(runButton).toBeDisabled();
513+
awaitexpect(exportButton).toBeDisabled();
496514
awaitexpect(page.locator("#sourceValidationMessage")).toContainText("Input is required");
497515

498516
expect(pageErrors).toEqual([]);
@@ -501,4 +519,26 @@ test.describe("Preview Generator V2 baseline", () => {
501519
awaitserver.close();
502520
}
503521
});
522+
523+
test("launches first-class tool starter template in workspace nav mode",async({ page })=>{
524+
constserver=awaitopenToolTemplate(page,"?launch=workspace");
525+
constpageErrors=[];
526+
527+
page.on("pageerror",(error)=>{
528+
pageErrors.push(error.message);
529+
});
530+
531+
try{
532+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeHidden();
533+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeVisible();
534+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toHaveAttribute("aria-label","Workspace actions");
535+
awaitexpect(page.locator("#workspaceImportManifestButton")).toHaveText("Import manifest");
536+
awaitexpect(page.locator("#workspaceCopyManifestButton")).toHaveText("Copy manifest");
537+
awaitexpect(page.locator("#workspaceExportManifestButton")).toHaveText("Export manifest");
538+
expect(pageErrors).toEqual([]);
539+
}finally{
540+
awaitcoverageReporter.stop(page);
541+
awaitserver.close();
542+
}
543+
});
504544
});

‎tools/templates/first-class-tool-starter/index.html‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,16 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
3636
</div>
3737
</details>
3838

39-
<navclass="tool-starter__menu" aria-label="Tool actions">
40-
<buttonid="runToolButton" type="button">Run</button>
41-
<buttonid="resetToolButton" type="button">Reset</button>
42-
<buttonid="exportToolStateButton" type="button">Export toolState</button>
39+
<navclass="tool-starter__menu tool-starter__tool__menu" aria-label="Tool actions" data-launch-mode-nav="tool">
40+
<buttonid="toolExportButton" type="button">Export</button>
41+
<buttonid="toolCopyJsonButton" type="button">Copy JSON</button>
42+
<buttonid="toolExportToolStateButton" type="button">Export toolState</button>
43+
</nav>
44+
45+
<navclass="tool-starter__menu tool-starter__workspace__menu" aria-label="Workspace actions" data-launch-mode-nav="workspace" hidden>
46+
<buttonid="workspaceImportManifestButton" type="button">Import manifest</button>
47+
<buttonid="workspaceCopyManifestButton" type="button">Copy manifest</button>
48+
<buttonid="workspaceExportManifestButton" type="button">Export manifest</button>
4349
</nav>
4450

4551
<mainclass="tool-starter app-shell" data-tool-id="first-class-tool-starter">
@@ -54,7 +60,7 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
5460
<span>Source value</span>
5561
<inputid="sourceInput" type="text" autocomplete="off" placeholder="Enter a value to process">
5662
</label>
57-
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Run can process.</p>
63+
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Export can process.</p>
5864
</div>
5965
</section>
6066

‎tools/templates/first-class-tool-starter/js/ToolStarterApp.js‎

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
exportclassToolStarterApp{
2-
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog }){
2+
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog, windowRef =window}){
33
this.accordions=accordions;
44
this.actionNav=actionNav;
55
this.inspector=inspector;
@@ -8,15 +8,21 @@ export class ToolStarterApp {
88
this.shell=shell;
99
this.sourceInput=sourceInput;
1010
this.statusLog=statusLog;
11+
this.window=windowRef;
1112
}
1213

1314
start(){
1415
this.shell.mount();
1516
this.accordions.forEach((accordion)=>accordion.mount());
1617
this.actionNav.mount({
17-
onExport: ()=>this.exportToolState(),
18-
onReset: ()=>this.reset(),
19-
onRun: ()=>this.run()
18+
onToolCopyJson: ()=>{
19+
voidthis.copyJson();
20+
},
21+
onToolExport: ()=>this.run(),
22+
onToolExportToolState: ()=>this.exportToolState(),
23+
onWorkspaceCopyManifest: ()=>this.statusLog.write("Copy manifest action ready for workspace wiring."),
24+
onWorkspaceExportManifest: ()=>this.statusLog.write("Export manifest action ready for workspace wiring."),
25+
onWorkspaceImportManifest: ()=>this.statusLog.write("Import manifest action ready for workspace wiring.")
2026
});
2127
this.sourceInput.mount({
2228
onChange: ()=>this.refreshActions()
@@ -66,11 +72,37 @@ export class ToolStarterApp {
6672
this.refreshActions();
6773
}
6874

75+
asynccopyJson(){
76+
constvalidation=this.sourceInput.validate();
77+
if(!validation.valid){
78+
this.statusLog.error(validation.message);
79+
this.refreshActions();
80+
return;
81+
}
82+
83+
consttoolState=this.serializer.createToolState({sourceValue: validation.value});
84+
this.inspector.showObject(toolState);
85+
constjson=JSON.stringify(toolState,null,2);
86+
if(typeofthis.window.navigator?.clipboard?.writeText!=="function"){
87+
this.statusLog.write("toolState JSON preview written to Output Summary. Clipboard API is unavailable.");
88+
this.refreshActions();
89+
return;
90+
}
91+
92+
try{
93+
awaitthis.window.navigator.clipboard.writeText(json);
94+
this.statusLog.write("toolState JSON copied.");
95+
}catch(error){
96+
this.statusLog.error(`Copy JSON failed: ${error.message}`);
97+
}
98+
this.refreshActions();
99+
}
100+
69101
refreshActions(){
70102
consthasValue=this.sourceInput.hasValue();
71103
if(!hasValue){
72-
this.sourceInput.showMessage("Input is required before Run can process.",false);
104+
this.sourceInput.showMessage("Input is required before Export can process.",false);
73105
}
74-
this.actionNav.setRunEnabled(hasValue);
106+
this.actionNav.setToolActionsEnabled(hasValue);
75107
}
76108
}

‎tools/templates/first-class-tool-starter/js/bootstrap.js‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,22 @@ window.addEventListener("DOMContentLoaded", () => {
2929
constapp=newToolStarterApp({
3030
accordions,
3131
actionNav: newActionNavControl({
32-
runButton: requireElement("#runToolButton"),
33-
resetButton: requireElement("#resetToolButton"),
34-
exportButton: requireElement("#exportToolStateButton")
32+
toolCopyJsonButton: requireElement("#toolCopyJsonButton"),
33+
toolExportButton: requireElement("#toolExportButton"),
34+
toolExportToolStateButton: requireElement("#toolExportToolStateButton"),
35+
toolNav: requireElement(".tool-starter__tool__menu"),
36+
workspaceCopyManifestButton: requireElement("#workspaceCopyManifestButton"),
37+
workspaceExportManifestButton: requireElement("#workspaceExportManifestButton"),
38+
workspaceImportManifestButton: requireElement("#workspaceImportManifestButton"),
39+
workspaceNav: requireElement(".tool-starter__workspace__menu")
3540
}),
3641
inspector: newInspectorControl(requireElement("#inspectorOutput")),
3742
preview: newPreviewPanelControl(requireElement("#previewOutput")),
3843
serializer: newToolStateSerializer("first-class-tool-starter"),
3944
shell: newToolStarterShellControl(),
4045
sourceInput,
41-
statusLog
46+
statusLog,
47+
windowRef: window
4248
});
4349

4450
app.start();

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add launch-mode-specific NAV behavior to first-class tool template - … · ToolboxAid/HTML-JavaScript-Gaming@ed61e63 · GitHub
Skip to content

Commit ed61e63

Browse files
committed
Add launch-mode-specific NAV behavior to first-class tool template - PR_26126_068-tool-template-launch-mode-nav
1 parent 0f067a9 commit ed61e63

10 files changed

Lines changed: 265 additions & 50 deletions

File tree

‎docs/dev/reports/playwright_v8_coverage_report.txt‎

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ Note: coverage entries are aggregated across every page/tool where coverageRepor
1313
Exercised tool entry points detected:
1414
(84%) Preview Generator V2 - exercised 19 runtime JS files
1515
(49%) Palette Manager - exercised 12 runtime JS files
16-
(89%) First-Class Tool Starter Template - exercised 10 runtime JS files
16+
(84%) First-Class Tool Starter Template - exercised 10 runtime JS files
1717
(0%) Workspace V2 - not exercised by this Playwright run
1818
(0%) Workspace Manager - not exercised by this Playwright run
1919

2020
Changed runtime JS files covered:
21-
(100%) none changed - no changed runtime JS files
21+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
22+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - executed lines 33/33; executed functions 7/8
23+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
24+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
2225

2326
Files with executed line/function counts where available:
2427
(2%) src/engine/input/ActionInputService.js - executed lines 397/397; executed functions 1/51
@@ -85,6 +88,7 @@ Files with executed line/function counts where available:
8588
(50%) tools/common/PaletteUsageService.js - executed lines 5/5; executed functions 1/2
8689
(50%) tools/palette-manager-v2/main.js - executed lines 88/88; executed functions 3/6
8790
(50%) tools/palette-manager-v2/modules/PaletteHistoryStack.js - executed lines 54/54; executed functions 5/10
91+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
8892
(50%) tools/toolRegistry.js - executed lines 425/425; executed functions 4/8
8993
(55%) tools/palette-manager-v2/modules/SwatchRow.js - executed lines 115/115; executed functions 6/11
9094
(56%) src/engine/logging/Logger.js - executed lines 56/56; executed functions 5/9
@@ -97,7 +101,6 @@ Files with executed line/function counts where available:
97101
(65%) tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js - executed lines 121/121; executed functions 15/23
98102
(67%) src/shared/number/numberUtils.js - executed lines 14/14; executed functions 2/3
99103
(67%) tools/preview-generator-v2/PreviewGeneratorV2ShellControl.js - executed lines 117/117; executed functions 8/12
100-
(67%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 69/69; executed functions 8/12
101104
(71%) tools/palette-manager-v2/controls/PaletteValidationErrorControl.js - executed lines 30/30; executed functions 5/7
102105
(73%) src/engine/theme/mount-shared-header.js - executed lines 143/143; executed functions 8/11
103106
(75%) src/engine/core/FixedTicker.js - executed lines 36/36; executed functions 3/4
@@ -146,15 +149,20 @@ Files with executed line/function counts where available:
146149
(100%) tools/preview-generator-v2/PreviewGeneratorV2Logger.js - executed lines 19/19; executed functions 5/5
147150
(100%) tools/preview-generator-v2/PreviewGeneratorV2RepoAccess.js - executed lines 21/21; executed functions 5/5
148151
(100%) tools/preview-generator-v2/PreviewGeneratorV2Ui.js - executed lines 48/48; executed functions 9/9
149-
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 42/42; executed functions 4/4
152+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
150153
(100%) tools/templates/first-class-tool-starter/js/controls/AccordionSection.js - executed lines 27/27; executed functions 5/5
151-
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 16/16; executed functions 4/4
154+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
152155
(100%) tools/templates/first-class-tool-starter/js/controls/InspectorControl.js - executed lines 8/8; executed functions 3/3
153156
(100%) tools/templates/first-class-tool-starter/js/controls/PreviewPanelControl.js - executed lines 23/23; executed functions 5/5
154157
(100%) tools/templates/first-class-tool-starter/js/services/ToolStateSerializer.js - executed lines 13/13; executed functions 3/3
155158

156159
Uncovered or low-coverage changed JS files:
157-
(100%) none changed - no changed runtime JS files
160+
(100%) none - no low-coverage changed runtime JS files
158161

159162
Changed JS files considered:
160-
(100%) none - no changed JS files
163+
(0%) tests/playwright/PreviewGeneratorV2Baseline.spec.mjs - changed JS file not collected as browser runtime coverage
164+
(0%) tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs - changed JS file not collected as browser runtime coverage
165+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - changed JS file with browser V8 coverage
166+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - changed JS file with browser V8 coverage
167+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - changed JS file with browser V8 coverage
168+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - changed JS file with browser V8 coverage
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
PR_26126_068-tool-template-launch-mode-nav
2+
3+
Scope:
4+
- Updated the official first-class tool starter at tools/templates/first-class-tool-starter/.
5+
- Updated existing Playwright coverage for the starter launch-mode NAV behavior.
6+
- No live tools, samples, schemas, roadmap, start_of_day folders, or tools/shared files were modified.
7+
8+
Changes:
9+
- Added separate tool-mode and workspace-mode NAV sections.
10+
- Default launch mode is tool mode.
11+
- ?launch=workspace switches visibility to the workspace NAV.
12+
- Tool NAV uses class tool-starter__tool__menu, aria-label Tool actions, and buttons:
13+
- Export
14+
- Copy JSON
15+
- Export toolState
16+
- Workspace NAV uses class tool-starter__workspace__menu, aria-label Workspace actions, and buttons:
17+
- Import manifest
18+
- Copy manifest
19+
- Export manifest
20+
- Added hidden-state CSS so only one NAV displays at a time.
21+
- Kept Preview Generator V2 color-match styling and src/engine/theme usage.
22+
- Preserved no tools/shared runtime dependency.
23+
- Preserved external CSS/JS only; no inline style block, inline script block, or inline event handlers were added.
24+
25+
Validation:
26+
- node --check tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js
27+
- node --check tools/templates/first-class-tool-starter/js/ToolStarterApp.js
28+
- node --check tools/templates/first-class-tool-starter/js/bootstrap.js
29+
- node --check tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js
30+
- node --check tests/playwright/PreviewGeneratorV2Baseline.spec.mjs
31+
- node --check tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs
32+
- rg check confirmed tools/templates/first-class-tool-starter/index.html has no inline style block, inline script block, or inline event handlers.
33+
- git diff --check
34+
- npm run test:workspace-v2
35+
- npx playwright test --config tools/templates/first-class-tool-starter/playwright.config.mjs --reporter=list
36+
37+
Playwright impacted: Yes
38+
- This PR changes template UI controls/interactions and launch-mode state.
39+
- npm run test:workspace-v2 passed.
40+
- Template-local Playwright passed.
41+
42+
Playwright behavior validated:
43+
- Official starter entry point defaults to tool NAV.
44+
- Official starter entry point with ?launch=workspace shows workspace NAV.
45+
- Only one launch-mode NAV is visible for each launch mode.
46+
- Tool and workspace NAV labels and button text match the required casing.
47+
- Duplicate button IDs are not present.
48+
- Existing starter accordion, required-field gating, primary action, and status clear behaviors still work.
49+
50+
Manual test notes:
51+
- Open tools/templates/first-class-tool-starter/index.html and confirm only Tool actions is visible with Export, Copy JSON, and Export toolState.
52+
- Open tools/templates/first-class-tool-starter/index.html?launch=workspace and confirm only Workspace actions is visible with Import manifest, Copy manifest, and Export manifest.
53+
- The official starter path is tools/templates/first-class-tool-starter/index.html; tools/templates/README.md designates that folder as the starter entry point.
54+
55+
Full samples smoke test:
56+
- Skipped. This PR only changes the first-class tool starter template and its targeted tests.

‎tests/playwright/PreviewGeneratorV2Baseline.spec.mjs‎

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ async function openPaletteManager(page) {
120120
returnserver;
121121
}
122122

123-
asyncfunctionopenToolTemplate(page){
123+
asyncfunctionopenToolTemplate(page,query=""){
124124
constserver=awaitstartRepoServer();
125125
awaitcoverageReporter.start(page);
126-
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html`,{waitUntil: "networkidle"});
126+
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html${query}`,{waitUntil: "networkidle"});
127127
returnserver;
128128
}
129129

@@ -466,6 +466,18 @@ test.describe("Preview Generator V2 baseline", () => {
466466
awaitexpect(page.locator("#shared-theme-header")).toBeAttached();
467467
awaitexpect(page.locator("[data-tool-starter-header]")).toContainText("First-Class Tool Starter");
468468
awaitexpect(page.locator("[data-tool-starter-summary]")).toHaveAttribute("data-tools-platform-summary-active","1");
469+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeVisible();
470+
awaitexpect(page.locator(".tool-starter__tool__menu")).toHaveAttribute("aria-label","Tool actions");
471+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeHidden();
472+
awaitexpect(page.locator("#toolExportButton")).toHaveText("Export");
473+
awaitexpect(page.locator("#toolCopyJsonButton")).toHaveText("Copy JSON");
474+
awaitexpect(page.locator("#toolExportToolStateButton")).toHaveText("Export toolState");
475+
476+
constduplicateIds=awaitpage.evaluate(()=>{
477+
constids=[...document.querySelectorAll("[id]")].map((element)=>element.id);
478+
returnids.filter((id,index)=>ids.indexOf(id)!==index);
479+
});
480+
expect(duplicateIds).toEqual([]);
469481

470482
constsharedReferences=awaitpage.evaluate(()=>[
471483
...document.querySelectorAll("script[src],link[href]")
@@ -482,17 +494,23 @@ test.describe("Preview Generator V2 baseline", () => {
482494

483495
awaitexpectAccordionToggles(page,"sourceInputContent");
484496

485-
construnButton=page.locator("#runToolButton");
486-
awaitexpect(runButton).toBeDisabled();
497+
constexportButton=page.locator("#toolExportButton");
498+
constcopyJsonButton=page.locator("#toolCopyJsonButton");
499+
constexportToolStateButton=page.locator("#toolExportToolStateButton");
500+
awaitexpect(exportButton).toBeDisabled();
501+
awaitexpect(copyJsonButton).toBeDisabled();
502+
awaitexpect(exportToolStateButton).toBeDisabled();
487503
awaitpage.locator("#sourceInput").fill("starter value");
488-
awaitexpect(runButton).toBeEnabled();
489-
awaitrunButton.click();
504+
awaitexpect(exportButton).toBeEnabled();
505+
awaitexpect(copyJsonButton).toBeEnabled();
506+
awaitexpect(exportToolStateButton).toBeEnabled();
507+
awaitexportButton.click();
490508
awaitexpect(page.locator("#statusLog")).toHaveValue(/Processedsourcevalue/);
491509
awaitpage.locator("#clearStatusButton").click();
492510
awaitexpect(page.locator("#statusLog")).toHaveValue("");
493511

494512
awaitpage.locator("#sourceInput").fill("");
495-
awaitexpect(runButton).toBeDisabled();
513+
awaitexpect(exportButton).toBeDisabled();
496514
awaitexpect(page.locator("#sourceValidationMessage")).toContainText("Input is required");
497515

498516
expect(pageErrors).toEqual([]);
@@ -501,4 +519,26 @@ test.describe("Preview Generator V2 baseline", () => {
501519
awaitserver.close();
502520
}
503521
});
522+
523+
test("launches first-class tool starter template in workspace nav mode",async({ page })=>{
524+
constserver=awaitopenToolTemplate(page,"?launch=workspace");
525+
constpageErrors=[];
526+
527+
page.on("pageerror",(error)=>{
528+
pageErrors.push(error.message);
529+
});
530+
531+
try{
532+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeHidden();
533+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeVisible();
534+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toHaveAttribute("aria-label","Workspace actions");
535+
awaitexpect(page.locator("#workspaceImportManifestButton")).toHaveText("Import manifest");
536+
awaitexpect(page.locator("#workspaceCopyManifestButton")).toHaveText("Copy manifest");
537+
awaitexpect(page.locator("#workspaceExportManifestButton")).toHaveText("Export manifest");
538+
expect(pageErrors).toEqual([]);
539+
}finally{
540+
awaitcoverageReporter.stop(page);
541+
awaitserver.close();
542+
}
543+
});
504544
});

‎tools/templates/first-class-tool-starter/index.html‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,16 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
3636
</div>
3737
</details>
3838

39-
<navclass="tool-starter__menu" aria-label="Tool actions">
40-
<buttonid="runToolButton" type="button">Run</button>
41-
<buttonid="resetToolButton" type="button">Reset</button>
42-
<buttonid="exportToolStateButton" type="button">Export toolState</button>
39+
<navclass="tool-starter__menu tool-starter__tool__menu" aria-label="Tool actions" data-launch-mode-nav="tool">
40+
<buttonid="toolExportButton" type="button">Export</button>
41+
<buttonid="toolCopyJsonButton" type="button">Copy JSON</button>
42+
<buttonid="toolExportToolStateButton" type="button">Export toolState</button>
43+
</nav>
44+
45+
<navclass="tool-starter__menu tool-starter__workspace__menu" aria-label="Workspace actions" data-launch-mode-nav="workspace" hidden>
46+
<buttonid="workspaceImportManifestButton" type="button">Import manifest</button>
47+
<buttonid="workspaceCopyManifestButton" type="button">Copy manifest</button>
48+
<buttonid="workspaceExportManifestButton" type="button">Export manifest</button>
4349
</nav>
4450

4551
<mainclass="tool-starter app-shell" data-tool-id="first-class-tool-starter">
@@ -54,7 +60,7 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
5460
<span>Source value</span>
5561
<inputid="sourceInput" type="text" autocomplete="off" placeholder="Enter a value to process">
5662
</label>
57-
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Run can process.</p>
63+
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Export can process.</p>
5864
</div>
5965
</section>
6066

‎tools/templates/first-class-tool-starter/js/ToolStarterApp.js‎

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
exportclassToolStarterApp{
2-
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog }){
2+
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog, windowRef =window}){
33
this.accordions=accordions;
44
this.actionNav=actionNav;
55
this.inspector=inspector;
@@ -8,15 +8,21 @@ export class ToolStarterApp {
88
this.shell=shell;
99
this.sourceInput=sourceInput;
1010
this.statusLog=statusLog;
11+
this.window=windowRef;
1112
}
1213

1314
start(){
1415
this.shell.mount();
1516
this.accordions.forEach((accordion)=>accordion.mount());
1617
this.actionNav.mount({
17-
onExport: ()=>this.exportToolState(),
18-
onReset: ()=>this.reset(),
19-
onRun: ()=>this.run()
18+
onToolCopyJson: ()=>{
19+
voidthis.copyJson();
20+
},
21+
onToolExport: ()=>this.run(),
22+
onToolExportToolState: ()=>this.exportToolState(),
23+
onWorkspaceCopyManifest: ()=>this.statusLog.write("Copy manifest action ready for workspace wiring."),
24+
onWorkspaceExportManifest: ()=>this.statusLog.write("Export manifest action ready for workspace wiring."),
25+
onWorkspaceImportManifest: ()=>this.statusLog.write("Import manifest action ready for workspace wiring.")
2026
});
2127
this.sourceInput.mount({
2228
onChange: ()=>this.refreshActions()
@@ -66,11 +72,37 @@ export class ToolStarterApp {
6672
this.refreshActions();
6773
}
6874

75+
asynccopyJson(){
76+
constvalidation=this.sourceInput.validate();
77+
if(!validation.valid){
78+
this.statusLog.error(validation.message);
79+
this.refreshActions();
80+
return;
81+
}
82+
83+
consttoolState=this.serializer.createToolState({sourceValue: validation.value});
84+
this.inspector.showObject(toolState);
85+
constjson=JSON.stringify(toolState,null,2);
86+
if(typeofthis.window.navigator?.clipboard?.writeText!=="function"){
87+
this.statusLog.write("toolState JSON preview written to Output Summary. Clipboard API is unavailable.");
88+
this.refreshActions();
89+
return;
90+
}
91+
92+
try{
93+
awaitthis.window.navigator.clipboard.writeText(json);
94+
this.statusLog.write("toolState JSON copied.");
95+
}catch(error){
96+
this.statusLog.error(`Copy JSON failed: ${error.message}`);
97+
}
98+
this.refreshActions();
99+
}
100+
69101
refreshActions(){
70102
consthasValue=this.sourceInput.hasValue();
71103
if(!hasValue){
72-
this.sourceInput.showMessage("Input is required before Run can process.",false);
104+
this.sourceInput.showMessage("Input is required before Export can process.",false);
73105
}
74-
this.actionNav.setRunEnabled(hasValue);
106+
this.actionNav.setToolActionsEnabled(hasValue);
75107
}
76108
}

‎tools/templates/first-class-tool-starter/js/bootstrap.js‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,22 @@ window.addEventListener("DOMContentLoaded", () => {
2929
constapp=newToolStarterApp({
3030
accordions,
3131
actionNav: newActionNavControl({
32-
runButton: requireElement("#runToolButton"),
33-
resetButton: requireElement("#resetToolButton"),
34-
exportButton: requireElement("#exportToolStateButton")
32+
toolCopyJsonButton: requireElement("#toolCopyJsonButton"),
33+
toolExportButton: requireElement("#toolExportButton"),
34+
toolExportToolStateButton: requireElement("#toolExportToolStateButton"),
35+
toolNav: requireElement(".tool-starter__tool__menu"),
36+
workspaceCopyManifestButton: requireElement("#workspaceCopyManifestButton"),
37+
workspaceExportManifestButton: requireElement("#workspaceExportManifestButton"),
38+
workspaceImportManifestButton: requireElement("#workspaceImportManifestButton"),
39+
workspaceNav: requireElement(".tool-starter__workspace__menu")
3540
}),
3641
inspector: newInspectorControl(requireElement("#inspectorOutput")),
3742
preview: newPreviewPanelControl(requireElement("#previewOutput")),
3843
serializer: newToolStateSerializer("first-class-tool-starter"),
3944
shell: newToolStarterShellControl(),
4045
sourceInput,
41-
statusLog
46+
statusLog,
47+
windowRef: window
4248
});
4349

4450
app.start();

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add launch-mode-specific NAV behavior to first-class tool template - … · ToolboxAid/HTML-JavaScript-Gaming@ed61e63 · GitHub
Skip to content

Commit ed61e63

Browse files
committed
Add launch-mode-specific NAV behavior to first-class tool template - PR_26126_068-tool-template-launch-mode-nav
1 parent 0f067a9 commit ed61e63

10 files changed

Lines changed: 265 additions & 50 deletions

File tree

‎docs/dev/reports/playwright_v8_coverage_report.txt‎

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ Note: coverage entries are aggregated across every page/tool where coverageRepor
1313
Exercised tool entry points detected:
1414
(84%) Preview Generator V2 - exercised 19 runtime JS files
1515
(49%) Palette Manager - exercised 12 runtime JS files
16-
(89%) First-Class Tool Starter Template - exercised 10 runtime JS files
16+
(84%) First-Class Tool Starter Template - exercised 10 runtime JS files
1717
(0%) Workspace V2 - not exercised by this Playwright run
1818
(0%) Workspace Manager - not exercised by this Playwright run
1919

2020
Changed runtime JS files covered:
21-
(100%) none changed - no changed runtime JS files
21+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
22+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - executed lines 33/33; executed functions 7/8
23+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
24+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
2225

2326
Files with executed line/function counts where available:
2427
(2%) src/engine/input/ActionInputService.js - executed lines 397/397; executed functions 1/51
@@ -85,6 +88,7 @@ Files with executed line/function counts where available:
8588
(50%) tools/common/PaletteUsageService.js - executed lines 5/5; executed functions 1/2
8689
(50%) tools/palette-manager-v2/main.js - executed lines 88/88; executed functions 3/6
8790
(50%) tools/palette-manager-v2/modules/PaletteHistoryStack.js - executed lines 54/54; executed functions 5/10
91+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
8892
(50%) tools/toolRegistry.js - executed lines 425/425; executed functions 4/8
8993
(55%) tools/palette-manager-v2/modules/SwatchRow.js - executed lines 115/115; executed functions 6/11
9094
(56%) src/engine/logging/Logger.js - executed lines 56/56; executed functions 5/9
@@ -97,7 +101,6 @@ Files with executed line/function counts where available:
97101
(65%) tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js - executed lines 121/121; executed functions 15/23
98102
(67%) src/shared/number/numberUtils.js - executed lines 14/14; executed functions 2/3
99103
(67%) tools/preview-generator-v2/PreviewGeneratorV2ShellControl.js - executed lines 117/117; executed functions 8/12
100-
(67%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 69/69; executed functions 8/12
101104
(71%) tools/palette-manager-v2/controls/PaletteValidationErrorControl.js - executed lines 30/30; executed functions 5/7
102105
(73%) src/engine/theme/mount-shared-header.js - executed lines 143/143; executed functions 8/11
103106
(75%) src/engine/core/FixedTicker.js - executed lines 36/36; executed functions 3/4
@@ -146,15 +149,20 @@ Files with executed line/function counts where available:
146149
(100%) tools/preview-generator-v2/PreviewGeneratorV2Logger.js - executed lines 19/19; executed functions 5/5
147150
(100%) tools/preview-generator-v2/PreviewGeneratorV2RepoAccess.js - executed lines 21/21; executed functions 5/5
148151
(100%) tools/preview-generator-v2/PreviewGeneratorV2Ui.js - executed lines 48/48; executed functions 9/9
149-
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 42/42; executed functions 4/4
152+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
150153
(100%) tools/templates/first-class-tool-starter/js/controls/AccordionSection.js - executed lines 27/27; executed functions 5/5
151-
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 16/16; executed functions 4/4
154+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
152155
(100%) tools/templates/first-class-tool-starter/js/controls/InspectorControl.js - executed lines 8/8; executed functions 3/3
153156
(100%) tools/templates/first-class-tool-starter/js/controls/PreviewPanelControl.js - executed lines 23/23; executed functions 5/5
154157
(100%) tools/templates/first-class-tool-starter/js/services/ToolStateSerializer.js - executed lines 13/13; executed functions 3/3
155158

156159
Uncovered or low-coverage changed JS files:
157-
(100%) none changed - no changed runtime JS files
160+
(100%) none - no low-coverage changed runtime JS files
158161

159162
Changed JS files considered:
160-
(100%) none - no changed JS files
163+
(0%) tests/playwright/PreviewGeneratorV2Baseline.spec.mjs - changed JS file not collected as browser runtime coverage
164+
(0%) tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs - changed JS file not collected as browser runtime coverage
165+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - changed JS file with browser V8 coverage
166+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - changed JS file with browser V8 coverage
167+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - changed JS file with browser V8 coverage
168+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - changed JS file with browser V8 coverage
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
PR_26126_068-tool-template-launch-mode-nav
2+
3+
Scope:
4+
- Updated the official first-class tool starter at tools/templates/first-class-tool-starter/.
5+
- Updated existing Playwright coverage for the starter launch-mode NAV behavior.
6+
- No live tools, samples, schemas, roadmap, start_of_day folders, or tools/shared files were modified.
7+
8+
Changes:
9+
- Added separate tool-mode and workspace-mode NAV sections.
10+
- Default launch mode is tool mode.
11+
- ?launch=workspace switches visibility to the workspace NAV.
12+
- Tool NAV uses class tool-starter__tool__menu, aria-label Tool actions, and buttons:
13+
- Export
14+
- Copy JSON
15+
- Export toolState
16+
- Workspace NAV uses class tool-starter__workspace__menu, aria-label Workspace actions, and buttons:
17+
- Import manifest
18+
- Copy manifest
19+
- Export manifest
20+
- Added hidden-state CSS so only one NAV displays at a time.
21+
- Kept Preview Generator V2 color-match styling and src/engine/theme usage.
22+
- Preserved no tools/shared runtime dependency.
23+
- Preserved external CSS/JS only; no inline style block, inline script block, or inline event handlers were added.
24+
25+
Validation:
26+
- node --check tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js
27+
- node --check tools/templates/first-class-tool-starter/js/ToolStarterApp.js
28+
- node --check tools/templates/first-class-tool-starter/js/bootstrap.js
29+
- node --check tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js
30+
- node --check tests/playwright/PreviewGeneratorV2Baseline.spec.mjs
31+
- node --check tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs
32+
- rg check confirmed tools/templates/first-class-tool-starter/index.html has no inline style block, inline script block, or inline event handlers.
33+
- git diff --check
34+
- npm run test:workspace-v2
35+
- npx playwright test --config tools/templates/first-class-tool-starter/playwright.config.mjs --reporter=list
36+
37+
Playwright impacted: Yes
38+
- This PR changes template UI controls/interactions and launch-mode state.
39+
- npm run test:workspace-v2 passed.
40+
- Template-local Playwright passed.
41+
42+
Playwright behavior validated:
43+
- Official starter entry point defaults to tool NAV.
44+
- Official starter entry point with ?launch=workspace shows workspace NAV.
45+
- Only one launch-mode NAV is visible for each launch mode.
46+
- Tool and workspace NAV labels and button text match the required casing.
47+
- Duplicate button IDs are not present.
48+
- Existing starter accordion, required-field gating, primary action, and status clear behaviors still work.
49+
50+
Manual test notes:
51+
- Open tools/templates/first-class-tool-starter/index.html and confirm only Tool actions is visible with Export, Copy JSON, and Export toolState.
52+
- Open tools/templates/first-class-tool-starter/index.html?launch=workspace and confirm only Workspace actions is visible with Import manifest, Copy manifest, and Export manifest.
53+
- The official starter path is tools/templates/first-class-tool-starter/index.html; tools/templates/README.md designates that folder as the starter entry point.
54+
55+
Full samples smoke test:
56+
- Skipped. This PR only changes the first-class tool starter template and its targeted tests.

‎tests/playwright/PreviewGeneratorV2Baseline.spec.mjs‎

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ async function openPaletteManager(page) {
120120
returnserver;
121121
}
122122

123-
asyncfunctionopenToolTemplate(page){
123+
asyncfunctionopenToolTemplate(page,query=""){
124124
constserver=awaitstartRepoServer();
125125
awaitcoverageReporter.start(page);
126-
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html`,{waitUntil: "networkidle"});
126+
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html${query}`,{waitUntil: "networkidle"});
127127
returnserver;
128128
}
129129

@@ -466,6 +466,18 @@ test.describe("Preview Generator V2 baseline", () => {
466466
awaitexpect(page.locator("#shared-theme-header")).toBeAttached();
467467
awaitexpect(page.locator("[data-tool-starter-header]")).toContainText("First-Class Tool Starter");
468468
awaitexpect(page.locator("[data-tool-starter-summary]")).toHaveAttribute("data-tools-platform-summary-active","1");
469+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeVisible();
470+
awaitexpect(page.locator(".tool-starter__tool__menu")).toHaveAttribute("aria-label","Tool actions");
471+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeHidden();
472+
awaitexpect(page.locator("#toolExportButton")).toHaveText("Export");
473+
awaitexpect(page.locator("#toolCopyJsonButton")).toHaveText("Copy JSON");
474+
awaitexpect(page.locator("#toolExportToolStateButton")).toHaveText("Export toolState");
475+
476+
constduplicateIds=awaitpage.evaluate(()=>{
477+
constids=[...document.querySelectorAll("[id]")].map((element)=>element.id);
478+
returnids.filter((id,index)=>ids.indexOf(id)!==index);
479+
});
480+
expect(duplicateIds).toEqual([]);
469481

470482
constsharedReferences=awaitpage.evaluate(()=>[
471483
...document.querySelectorAll("script[src],link[href]")
@@ -482,17 +494,23 @@ test.describe("Preview Generator V2 baseline", () => {
482494

483495
awaitexpectAccordionToggles(page,"sourceInputContent");
484496

485-
construnButton=page.locator("#runToolButton");
486-
awaitexpect(runButton).toBeDisabled();
497+
constexportButton=page.locator("#toolExportButton");
498+
constcopyJsonButton=page.locator("#toolCopyJsonButton");
499+
constexportToolStateButton=page.locator("#toolExportToolStateButton");
500+
awaitexpect(exportButton).toBeDisabled();
501+
awaitexpect(copyJsonButton).toBeDisabled();
502+
awaitexpect(exportToolStateButton).toBeDisabled();
487503
awaitpage.locator("#sourceInput").fill("starter value");
488-
awaitexpect(runButton).toBeEnabled();
489-
awaitrunButton.click();
504+
awaitexpect(exportButton).toBeEnabled();
505+
awaitexpect(copyJsonButton).toBeEnabled();
506+
awaitexpect(exportToolStateButton).toBeEnabled();
507+
awaitexportButton.click();
490508
awaitexpect(page.locator("#statusLog")).toHaveValue(/Processedsourcevalue/);
491509
awaitpage.locator("#clearStatusButton").click();
492510
awaitexpect(page.locator("#statusLog")).toHaveValue("");
493511

494512
awaitpage.locator("#sourceInput").fill("");
495-
awaitexpect(runButton).toBeDisabled();
513+
awaitexpect(exportButton).toBeDisabled();
496514
awaitexpect(page.locator("#sourceValidationMessage")).toContainText("Input is required");
497515

498516
expect(pageErrors).toEqual([]);
@@ -501,4 +519,26 @@ test.describe("Preview Generator V2 baseline", () => {
501519
awaitserver.close();
502520
}
503521
});
522+
523+
test("launches first-class tool starter template in workspace nav mode",async({ page })=>{
524+
constserver=awaitopenToolTemplate(page,"?launch=workspace");
525+
constpageErrors=[];
526+
527+
page.on("pageerror",(error)=>{
528+
pageErrors.push(error.message);
529+
});
530+
531+
try{
532+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeHidden();
533+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeVisible();
534+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toHaveAttribute("aria-label","Workspace actions");
535+
awaitexpect(page.locator("#workspaceImportManifestButton")).toHaveText("Import manifest");
536+
awaitexpect(page.locator("#workspaceCopyManifestButton")).toHaveText("Copy manifest");
537+
awaitexpect(page.locator("#workspaceExportManifestButton")).toHaveText("Export manifest");
538+
expect(pageErrors).toEqual([]);
539+
}finally{
540+
awaitcoverageReporter.stop(page);
541+
awaitserver.close();
542+
}
543+
});
504544
});

‎tools/templates/first-class-tool-starter/index.html‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,16 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
3636
</div>
3737
</details>
3838

39-
<navclass="tool-starter__menu" aria-label="Tool actions">
40-
<buttonid="runToolButton" type="button">Run</button>
41-
<buttonid="resetToolButton" type="button">Reset</button>
42-
<buttonid="exportToolStateButton" type="button">Export toolState</button>
39+
<navclass="tool-starter__menu tool-starter__tool__menu" aria-label="Tool actions" data-launch-mode-nav="tool">
40+
<buttonid="toolExportButton" type="button">Export</button>
41+
<buttonid="toolCopyJsonButton" type="button">Copy JSON</button>
42+
<buttonid="toolExportToolStateButton" type="button">Export toolState</button>
43+
</nav>
44+
45+
<navclass="tool-starter__menu tool-starter__workspace__menu" aria-label="Workspace actions" data-launch-mode-nav="workspace" hidden>
46+
<buttonid="workspaceImportManifestButton" type="button">Import manifest</button>
47+
<buttonid="workspaceCopyManifestButton" type="button">Copy manifest</button>
48+
<buttonid="workspaceExportManifestButton" type="button">Export manifest</button>
4349
</nav>
4450

4551
<mainclass="tool-starter app-shell" data-tool-id="first-class-tool-starter">
@@ -54,7 +60,7 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
5460
<span>Source value</span>
5561
<inputid="sourceInput" type="text" autocomplete="off" placeholder="Enter a value to process">
5662
</label>
57-
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Run can process.</p>
63+
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Export can process.</p>
5864
</div>
5965
</section>
6066

‎tools/templates/first-class-tool-starter/js/ToolStarterApp.js‎

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
exportclassToolStarterApp{
2-
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog }){
2+
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog, windowRef =window}){
33
this.accordions=accordions;
44
this.actionNav=actionNav;
55
this.inspector=inspector;
@@ -8,15 +8,21 @@ export class ToolStarterApp {
88
this.shell=shell;
99
this.sourceInput=sourceInput;
1010
this.statusLog=statusLog;
11+
this.window=windowRef;
1112
}
1213

1314
start(){
1415
this.shell.mount();
1516
this.accordions.forEach((accordion)=>accordion.mount());
1617
this.actionNav.mount({
17-
onExport: ()=>this.exportToolState(),
18-
onReset: ()=>this.reset(),
19-
onRun: ()=>this.run()
18+
onToolCopyJson: ()=>{
19+
voidthis.copyJson();
20+
},
21+
onToolExport: ()=>this.run(),
22+
onToolExportToolState: ()=>this.exportToolState(),
23+
onWorkspaceCopyManifest: ()=>this.statusLog.write("Copy manifest action ready for workspace wiring."),
24+
onWorkspaceExportManifest: ()=>this.statusLog.write("Export manifest action ready for workspace wiring."),
25+
onWorkspaceImportManifest: ()=>this.statusLog.write("Import manifest action ready for workspace wiring.")
2026
});
2127
this.sourceInput.mount({
2228
onChange: ()=>this.refreshActions()
@@ -66,11 +72,37 @@ export class ToolStarterApp {
6672
this.refreshActions();
6773
}
6874

75+
asynccopyJson(){
76+
constvalidation=this.sourceInput.validate();
77+
if(!validation.valid){
78+
this.statusLog.error(validation.message);
79+
this.refreshActions();
80+
return;
81+
}
82+
83+
consttoolState=this.serializer.createToolState({sourceValue: validation.value});
84+
this.inspector.showObject(toolState);
85+
constjson=JSON.stringify(toolState,null,2);
86+
if(typeofthis.window.navigator?.clipboard?.writeText!=="function"){
87+
this.statusLog.write("toolState JSON preview written to Output Summary. Clipboard API is unavailable.");
88+
this.refreshActions();
89+
return;
90+
}
91+
92+
try{
93+
awaitthis.window.navigator.clipboard.writeText(json);
94+
this.statusLog.write("toolState JSON copied.");
95+
}catch(error){
96+
this.statusLog.error(`Copy JSON failed: ${error.message}`);
97+
}
98+
this.refreshActions();
99+
}
100+
69101
refreshActions(){
70102
consthasValue=this.sourceInput.hasValue();
71103
if(!hasValue){
72-
this.sourceInput.showMessage("Input is required before Run can process.",false);
104+
this.sourceInput.showMessage("Input is required before Export can process.",false);
73105
}
74-
this.actionNav.setRunEnabled(hasValue);
106+
this.actionNav.setToolActionsEnabled(hasValue);
75107
}
76108
}

‎tools/templates/first-class-tool-starter/js/bootstrap.js‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,22 @@ window.addEventListener("DOMContentLoaded", () => {
2929
constapp=newToolStarterApp({
3030
accordions,
3131
actionNav: newActionNavControl({
32-
runButton: requireElement("#runToolButton"),
33-
resetButton: requireElement("#resetToolButton"),
34-
exportButton: requireElement("#exportToolStateButton")
32+
toolCopyJsonButton: requireElement("#toolCopyJsonButton"),
33+
toolExportButton: requireElement("#toolExportButton"),
34+
toolExportToolStateButton: requireElement("#toolExportToolStateButton"),
35+
toolNav: requireElement(".tool-starter__tool__menu"),
36+
workspaceCopyManifestButton: requireElement("#workspaceCopyManifestButton"),
37+
workspaceExportManifestButton: requireElement("#workspaceExportManifestButton"),
38+
workspaceImportManifestButton: requireElement("#workspaceImportManifestButton"),
39+
workspaceNav: requireElement(".tool-starter__workspace__menu")
3540
}),
3641
inspector: newInspectorControl(requireElement("#inspectorOutput")),
3742
preview: newPreviewPanelControl(requireElement("#previewOutput")),
3843
serializer: newToolStateSerializer("first-class-tool-starter"),
3944
shell: newToolStarterShellControl(),
4045
sourceInput,
41-
statusLog
46+
statusLog,
47+
windowRef: window
4248
});
4349

4450
app.start();

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Add launch-mode-specific NAV behavior to first-class tool template - … · ToolboxAid/HTML-JavaScript-Gaming@ed61e63 · GitHub
Skip to content

Commit ed61e63

Browse files
committed
Add launch-mode-specific NAV behavior to first-class tool template - PR_26126_068-tool-template-launch-mode-nav
1 parent 0f067a9 commit ed61e63

10 files changed

Lines changed: 265 additions & 50 deletions

File tree

‎docs/dev/reports/playwright_v8_coverage_report.txt‎

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ Note: coverage entries are aggregated across every page/tool where coverageRepor
1313
Exercised tool entry points detected:
1414
(84%) Preview Generator V2 - exercised 19 runtime JS files
1515
(49%) Palette Manager - exercised 12 runtime JS files
16-
(89%) First-Class Tool Starter Template - exercised 10 runtime JS files
16+
(84%) First-Class Tool Starter Template - exercised 10 runtime JS files
1717
(0%) Workspace V2 - not exercised by this Playwright run
1818
(0%) Workspace Manager - not exercised by this Playwright run
1919

2020
Changed runtime JS files covered:
21-
(100%) none changed - no changed runtime JS files
21+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
22+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - executed lines 33/33; executed functions 7/8
23+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
24+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
2225

2326
Files with executed line/function counts where available:
2427
(2%) src/engine/input/ActionInputService.js - executed lines 397/397; executed functions 1/51
@@ -85,6 +88,7 @@ Files with executed line/function counts where available:
8588
(50%) tools/common/PaletteUsageService.js - executed lines 5/5; executed functions 1/2
8689
(50%) tools/palette-manager-v2/main.js - executed lines 88/88; executed functions 3/6
8790
(50%) tools/palette-manager-v2/modules/PaletteHistoryStack.js - executed lines 54/54; executed functions 5/10
91+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 98/98; executed functions 8/16
8892
(50%) tools/toolRegistry.js - executed lines 425/425; executed functions 4/8
8993
(55%) tools/palette-manager-v2/modules/SwatchRow.js - executed lines 115/115; executed functions 6/11
9094
(56%) src/engine/logging/Logger.js - executed lines 56/56; executed functions 5/9
@@ -97,7 +101,6 @@ Files with executed line/function counts where available:
97101
(65%) tools/palette-manager-v2/controls/SourcePaletteBrowserControl.js - executed lines 121/121; executed functions 15/23
98102
(67%) src/shared/number/numberUtils.js - executed lines 14/14; executed functions 2/3
99103
(67%) tools/preview-generator-v2/PreviewGeneratorV2ShellControl.js - executed lines 117/117; executed functions 8/12
100-
(67%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - executed lines 69/69; executed functions 8/12
101104
(71%) tools/palette-manager-v2/controls/PaletteValidationErrorControl.js - executed lines 30/30; executed functions 5/7
102105
(73%) src/engine/theme/mount-shared-header.js - executed lines 143/143; executed functions 8/11
103106
(75%) src/engine/core/FixedTicker.js - executed lines 36/36; executed functions 3/4
@@ -146,15 +149,20 @@ Files with executed line/function counts where available:
146149
(100%) tools/preview-generator-v2/PreviewGeneratorV2Logger.js - executed lines 19/19; executed functions 5/5
147150
(100%) tools/preview-generator-v2/PreviewGeneratorV2RepoAccess.js - executed lines 21/21; executed functions 5/5
148151
(100%) tools/preview-generator-v2/PreviewGeneratorV2Ui.js - executed lines 48/48; executed functions 9/9
149-
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 42/42; executed functions 4/4
152+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - executed lines 48/48; executed functions 4/4
150153
(100%) tools/templates/first-class-tool-starter/js/controls/AccordionSection.js - executed lines 27/27; executed functions 5/5
151-
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 16/16; executed functions 4/4
154+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - executed lines 51/51; executed functions 5/5
152155
(100%) tools/templates/first-class-tool-starter/js/controls/InspectorControl.js - executed lines 8/8; executed functions 3/3
153156
(100%) tools/templates/first-class-tool-starter/js/controls/PreviewPanelControl.js - executed lines 23/23; executed functions 5/5
154157
(100%) tools/templates/first-class-tool-starter/js/services/ToolStateSerializer.js - executed lines 13/13; executed functions 3/3
155158

156159
Uncovered or low-coverage changed JS files:
157-
(100%) none changed - no changed runtime JS files
160+
(100%) none - no low-coverage changed runtime JS files
158161

159162
Changed JS files considered:
160-
(100%) none - no changed JS files
163+
(0%) tests/playwright/PreviewGeneratorV2Baseline.spec.mjs - changed JS file not collected as browser runtime coverage
164+
(0%) tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs - changed JS file not collected as browser runtime coverage
165+
(50%) tools/templates/first-class-tool-starter/js/ToolStarterApp.js - changed JS file with browser V8 coverage
166+
(88%) tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js - changed JS file with browser V8 coverage
167+
(100%) tools/templates/first-class-tool-starter/js/bootstrap.js - changed JS file with browser V8 coverage
168+
(100%) tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js - changed JS file with browser V8 coverage
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
PR_26126_068-tool-template-launch-mode-nav
2+
3+
Scope:
4+
- Updated the official first-class tool starter at tools/templates/first-class-tool-starter/.
5+
- Updated existing Playwright coverage for the starter launch-mode NAV behavior.
6+
- No live tools, samples, schemas, roadmap, start_of_day folders, or tools/shared files were modified.
7+
8+
Changes:
9+
- Added separate tool-mode and workspace-mode NAV sections.
10+
- Default launch mode is tool mode.
11+
- ?launch=workspace switches visibility to the workspace NAV.
12+
- Tool NAV uses class tool-starter__tool__menu, aria-label Tool actions, and buttons:
13+
- Export
14+
- Copy JSON
15+
- Export toolState
16+
- Workspace NAV uses class tool-starter__workspace__menu, aria-label Workspace actions, and buttons:
17+
- Import manifest
18+
- Copy manifest
19+
- Export manifest
20+
- Added hidden-state CSS so only one NAV displays at a time.
21+
- Kept Preview Generator V2 color-match styling and src/engine/theme usage.
22+
- Preserved no tools/shared runtime dependency.
23+
- Preserved external CSS/JS only; no inline style block, inline script block, or inline event handlers were added.
24+
25+
Validation:
26+
- node --check tools/templates/first-class-tool-starter/js/controls/ActionNavControl.js
27+
- node --check tools/templates/first-class-tool-starter/js/ToolStarterApp.js
28+
- node --check tools/templates/first-class-tool-starter/js/bootstrap.js
29+
- node --check tools/templates/first-class-tool-starter/js/controls/SourceInputControl.js
30+
- node --check tests/playwright/PreviewGeneratorV2Baseline.spec.mjs
31+
- node --check tools/templates/first-class-tool-starter/tests/playwright/FirstClassToolStarter.spec.mjs
32+
- rg check confirmed tools/templates/first-class-tool-starter/index.html has no inline style block, inline script block, or inline event handlers.
33+
- git diff --check
34+
- npm run test:workspace-v2
35+
- npx playwright test --config tools/templates/first-class-tool-starter/playwright.config.mjs --reporter=list
36+
37+
Playwright impacted: Yes
38+
- This PR changes template UI controls/interactions and launch-mode state.
39+
- npm run test:workspace-v2 passed.
40+
- Template-local Playwright passed.
41+
42+
Playwright behavior validated:
43+
- Official starter entry point defaults to tool NAV.
44+
- Official starter entry point with ?launch=workspace shows workspace NAV.
45+
- Only one launch-mode NAV is visible for each launch mode.
46+
- Tool and workspace NAV labels and button text match the required casing.
47+
- Duplicate button IDs are not present.
48+
- Existing starter accordion, required-field gating, primary action, and status clear behaviors still work.
49+
50+
Manual test notes:
51+
- Open tools/templates/first-class-tool-starter/index.html and confirm only Tool actions is visible with Export, Copy JSON, and Export toolState.
52+
- Open tools/templates/first-class-tool-starter/index.html?launch=workspace and confirm only Workspace actions is visible with Import manifest, Copy manifest, and Export manifest.
53+
- The official starter path is tools/templates/first-class-tool-starter/index.html; tools/templates/README.md designates that folder as the starter entry point.
54+
55+
Full samples smoke test:
56+
- Skipped. This PR only changes the first-class tool starter template and its targeted tests.

‎tests/playwright/PreviewGeneratorV2Baseline.spec.mjs‎

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ async function openPaletteManager(page) {
120120
returnserver;
121121
}
122122

123-
asyncfunctionopenToolTemplate(page){
123+
asyncfunctionopenToolTemplate(page,query=""){
124124
constserver=awaitstartRepoServer();
125125
awaitcoverageReporter.start(page);
126-
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html`,{waitUntil: "networkidle"});
126+
awaitpage.goto(`${server.baseUrl}/tools/templates/first-class-tool-starter/index.html${query}`,{waitUntil: "networkidle"});
127127
returnserver;
128128
}
129129

@@ -466,6 +466,18 @@ test.describe("Preview Generator V2 baseline", () => {
466466
awaitexpect(page.locator("#shared-theme-header")).toBeAttached();
467467
awaitexpect(page.locator("[data-tool-starter-header]")).toContainText("First-Class Tool Starter");
468468
awaitexpect(page.locator("[data-tool-starter-summary]")).toHaveAttribute("data-tools-platform-summary-active","1");
469+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeVisible();
470+
awaitexpect(page.locator(".tool-starter__tool__menu")).toHaveAttribute("aria-label","Tool actions");
471+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeHidden();
472+
awaitexpect(page.locator("#toolExportButton")).toHaveText("Export");
473+
awaitexpect(page.locator("#toolCopyJsonButton")).toHaveText("Copy JSON");
474+
awaitexpect(page.locator("#toolExportToolStateButton")).toHaveText("Export toolState");
475+
476+
constduplicateIds=awaitpage.evaluate(()=>{
477+
constids=[...document.querySelectorAll("[id]")].map((element)=>element.id);
478+
returnids.filter((id,index)=>ids.indexOf(id)!==index);
479+
});
480+
expect(duplicateIds).toEqual([]);
469481

470482
constsharedReferences=awaitpage.evaluate(()=>[
471483
...document.querySelectorAll("script[src],link[href]")
@@ -482,17 +494,23 @@ test.describe("Preview Generator V2 baseline", () => {
482494

483495
awaitexpectAccordionToggles(page,"sourceInputContent");
484496

485-
construnButton=page.locator("#runToolButton");
486-
awaitexpect(runButton).toBeDisabled();
497+
constexportButton=page.locator("#toolExportButton");
498+
constcopyJsonButton=page.locator("#toolCopyJsonButton");
499+
constexportToolStateButton=page.locator("#toolExportToolStateButton");
500+
awaitexpect(exportButton).toBeDisabled();
501+
awaitexpect(copyJsonButton).toBeDisabled();
502+
awaitexpect(exportToolStateButton).toBeDisabled();
487503
awaitpage.locator("#sourceInput").fill("starter value");
488-
awaitexpect(runButton).toBeEnabled();
489-
awaitrunButton.click();
504+
awaitexpect(exportButton).toBeEnabled();
505+
awaitexpect(copyJsonButton).toBeEnabled();
506+
awaitexpect(exportToolStateButton).toBeEnabled();
507+
awaitexportButton.click();
490508
awaitexpect(page.locator("#statusLog")).toHaveValue(/Processedsourcevalue/);
491509
awaitpage.locator("#clearStatusButton").click();
492510
awaitexpect(page.locator("#statusLog")).toHaveValue("");
493511

494512
awaitpage.locator("#sourceInput").fill("");
495-
awaitexpect(runButton).toBeDisabled();
513+
awaitexpect(exportButton).toBeDisabled();
496514
awaitexpect(page.locator("#sourceValidationMessage")).toContainText("Input is required");
497515

498516
expect(pageErrors).toEqual([]);
@@ -501,4 +519,26 @@ test.describe("Preview Generator V2 baseline", () => {
501519
awaitserver.close();
502520
}
503521
});
522+
523+
test("launches first-class tool starter template in workspace nav mode",async({ page })=>{
524+
constserver=awaitopenToolTemplate(page,"?launch=workspace");
525+
constpageErrors=[];
526+
527+
page.on("pageerror",(error)=>{
528+
pageErrors.push(error.message);
529+
});
530+
531+
try{
532+
awaitexpect(page.locator(".tool-starter__tool__menu")).toBeHidden();
533+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toBeVisible();
534+
awaitexpect(page.locator(".tool-starter__workspace__menu")).toHaveAttribute("aria-label","Workspace actions");
535+
awaitexpect(page.locator("#workspaceImportManifestButton")).toHaveText("Import manifest");
536+
awaitexpect(page.locator("#workspaceCopyManifestButton")).toHaveText("Copy manifest");
537+
awaitexpect(page.locator("#workspaceExportManifestButton")).toHaveText("Export manifest");
538+
expect(pageErrors).toEqual([]);
539+
}finally{
540+
awaitcoverageReporter.stop(page);
541+
awaitserver.close();
542+
}
543+
});
504544
});

‎tools/templates/first-class-tool-starter/index.html‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,16 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
3636
</div>
3737
</details>
3838

39-
<navclass="tool-starter__menu" aria-label="Tool actions">
40-
<buttonid="runToolButton" type="button">Run</button>
41-
<buttonid="resetToolButton" type="button">Reset</button>
42-
<buttonid="exportToolStateButton" type="button">Export toolState</button>
39+
<navclass="tool-starter__menu tool-starter__tool__menu" aria-label="Tool actions" data-launch-mode-nav="tool">
40+
<buttonid="toolExportButton" type="button">Export</button>
41+
<buttonid="toolCopyJsonButton" type="button">Copy JSON</button>
42+
<buttonid="toolExportToolStateButton" type="button">Export toolState</button>
43+
</nav>
44+
45+
<navclass="tool-starter__menu tool-starter__workspace__menu" aria-label="Workspace actions" data-launch-mode-nav="workspace" hidden>
46+
<buttonid="workspaceImportManifestButton" type="button">Import manifest</button>
47+
<buttonid="workspaceCopyManifestButton" type="button">Copy manifest</button>
48+
<buttonid="workspaceExportManifestButton" type="button">Export manifest</button>
4349
</nav>
4450

4551
<mainclass="tool-starter app-shell" data-tool-id="first-class-tool-starter">
@@ -54,7 +60,7 @@ <h2 class="tools-platform-frame__eyebrow">First-Class Tools Surface</h2>
5460
<span>Source value</span>
5561
<inputid="sourceInput" type="text" autocomplete="off" placeholder="Enter a value to process">
5662
</label>
57-
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Run can process.</p>
63+
<pid="sourceValidationMessage" class="tool-starter__hint">Input is required before Export can process.</p>
5864
</div>
5965
</section>
6066

‎tools/templates/first-class-tool-starter/js/ToolStarterApp.js‎

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
exportclassToolStarterApp{
2-
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog }){
2+
constructor({ accordions, actionNav, inspector, preview, serializer, shell, sourceInput, statusLog, windowRef =window}){
33
this.accordions=accordions;
44
this.actionNav=actionNav;
55
this.inspector=inspector;
@@ -8,15 +8,21 @@ export class ToolStarterApp {
88
this.shell=shell;
99
this.sourceInput=sourceInput;
1010
this.statusLog=statusLog;
11+
this.window=windowRef;
1112
}
1213

1314
start(){
1415
this.shell.mount();
1516
this.accordions.forEach((accordion)=>accordion.mount());
1617
this.actionNav.mount({
17-
onExport: ()=>this.exportToolState(),
18-
onReset: ()=>this.reset(),
19-
onRun: ()=>this.run()
18+
onToolCopyJson: ()=>{
19+
voidthis.copyJson();
20+
},
21+
onToolExport: ()=>this.run(),
22+
onToolExportToolState: ()=>this.exportToolState(),
23+
onWorkspaceCopyManifest: ()=>this.statusLog.write("Copy manifest action ready for workspace wiring."),
24+
onWorkspaceExportManifest: ()=>this.statusLog.write("Export manifest action ready for workspace wiring."),
25+
onWorkspaceImportManifest: ()=>this.statusLog.write("Import manifest action ready for workspace wiring.")
2026
});
2127
this.sourceInput.mount({
2228
onChange: ()=>this.refreshActions()
@@ -66,11 +72,37 @@ export class ToolStarterApp {
6672
this.refreshActions();
6773
}
6874

75+
asynccopyJson(){
76+
constvalidation=this.sourceInput.validate();
77+
if(!validation.valid){
78+
this.statusLog.error(validation.message);
79+
this.refreshActions();
80+
return;
81+
}
82+
83+
consttoolState=this.serializer.createToolState({sourceValue: validation.value});
84+
this.inspector.showObject(toolState);
85+
constjson=JSON.stringify(toolState,null,2);
86+
if(typeofthis.window.navigator?.clipboard?.writeText!=="function"){
87+
this.statusLog.write("toolState JSON preview written to Output Summary. Clipboard API is unavailable.");
88+
this.refreshActions();
89+
return;
90+
}
91+
92+
try{
93+
awaitthis.window.navigator.clipboard.writeText(json);
94+
this.statusLog.write("toolState JSON copied.");
95+
}catch(error){
96+
this.statusLog.error(`Copy JSON failed: ${error.message}`);
97+
}
98+
this.refreshActions();
99+
}
100+
69101
refreshActions(){
70102
consthasValue=this.sourceInput.hasValue();
71103
if(!hasValue){
72-
this.sourceInput.showMessage("Input is required before Run can process.",false);
104+
this.sourceInput.showMessage("Input is required before Export can process.",false);
73105
}
74-
this.actionNav.setRunEnabled(hasValue);
106+
this.actionNav.setToolActionsEnabled(hasValue);
75107
}
76108
}

‎tools/templates/first-class-tool-starter/js/bootstrap.js‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,22 @@ window.addEventListener("DOMContentLoaded", () => {
2929
constapp=newToolStarterApp({
3030
accordions,
3131
actionNav: newActionNavControl({
32-
runButton: requireElement("#runToolButton"),
33-
resetButton: requireElement("#resetToolButton"),
34-
exportButton: requireElement("#exportToolStateButton")
32+
toolCopyJsonButton: requireElement("#toolCopyJsonButton"),
33+
toolExportButton: requireElement("#toolExportButton"),
34+
toolExportToolStateButton: requireElement("#toolExportToolStateButton"),
35+
toolNav: requireElement(".tool-starter__tool__menu"),
36+
workspaceCopyManifestButton: requireElement("#workspaceCopyManifestButton"),
37+
workspaceExportManifestButton: requireElement("#workspaceExportManifestButton"),
38+
workspaceImportManifestButton: requireElement("#workspaceImportManifestButton"),
39+
workspaceNav: requireElement(".tool-starter__workspace__menu")
3540
}),
3641
inspector: newInspectorControl(requireElement("#inspectorOutput")),
3742
preview: newPreviewPanelControl(requireElement("#previewOutput")),
3843
serializer: newToolStateSerializer("first-class-tool-starter"),
3944
shell: newToolStarterShellControl(),
4045
sourceInput,
41-
statusLog
46+
statusLog,
47+
windowRef: window
4248
});
4349

4450
app.start();

0 commit comments

Comments
 (0)