dialog-ide 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +69 -0
- package/LICENSE +201 -0
- package/README.md +211 -0
- package/THIRD_PARTY_LICENSES.md +157 -0
- package/bin/darwin-arm64/aambundle +0 -0
- package/bin/darwin-arm64/dgdebug +0 -0
- package/bin/darwin-arm64/dialogc +0 -0
- package/bin/dialog-lib/stddebug.dg +112 -0
- package/bin/dialog-lib/stdlib.dg +6730 -0
- package/bin/dialog-lib/unit.dg +187 -0
- package/dist/cli/commands/run-skein.d.ts +17 -0
- package/dist/cli/commands/run-skein.d.ts.map +1 -0
- package/dist/cli/commands/run-skein.js +105 -0
- package/dist/cli/commands/run-skein.js.map +1 -0
- package/dist/cli/commands/sources.d.ts +12 -0
- package/dist/cli/commands/sources.d.ts.map +1 -0
- package/dist/cli/commands/sources.js +42 -0
- package/dist/cli/commands/sources.js.map +1 -0
- package/dist/cli/commands/test.d.ts +9 -0
- package/dist/cli/commands/test.d.ts.map +1 -0
- package/dist/cli/commands/test.js +41 -0
- package/dist/cli/commands/test.js.map +1 -0
- package/dist/cli/context.d.ts +9 -0
- package/dist/cli/context.d.ts.map +1 -0
- package/dist/cli/context.js +78 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +32 -0
- package/dist/cli.js.map +1 -0
- package/dist/dialog-cover-resize.d.ts +2 -0
- package/dist/dialog-cover-resize.d.ts.map +1 -0
- package/dist/dialog-cover-resize.js +46 -0
- package/dist/dialog-cover-resize.js.map +1 -0
- package/dist/dialog-export.d.ts +23 -0
- package/dist/dialog-export.d.ts.map +1 -0
- package/dist/dialog-export.js +133 -0
- package/dist/dialog-export.js.map +1 -0
- package/dist/dialog-feelies.d.ts +4 -0
- package/dist/dialog-feelies.d.ts.map +1 -0
- package/dist/dialog-feelies.js +62 -0
- package/dist/dialog-feelies.js.map +1 -0
- package/dist/dialog-outline.d.ts +19 -0
- package/dist/dialog-outline.d.ts.map +1 -0
- package/dist/dialog-outline.js +122 -0
- package/dist/dialog-outline.js.map +1 -0
- package/dist/dialog-project-init.d.ts +5 -0
- package/dist/dialog-project-init.d.ts.map +1 -0
- package/dist/dialog-project-init.js +107 -0
- package/dist/dialog-project-init.js.map +1 -0
- package/dist/dialog-source-coverage.d.ts +9 -0
- package/dist/dialog-source-coverage.d.ts.map +1 -0
- package/dist/dialog-source-coverage.js +61 -0
- package/dist/dialog-source-coverage.js.map +1 -0
- package/dist/dialog-source-decoration-provider.d.ts +11 -0
- package/dist/dialog-source-decoration-provider.d.ts.map +1 -0
- package/dist/dialog-source-decoration-provider.js +79 -0
- package/dist/dialog-source-decoration-provider.js.map +1 -0
- package/dist/dialog-symbol-index.d.ts +19 -0
- package/dist/dialog-symbol-index.d.ts.map +1 -0
- package/dist/dialog-symbol-index.js +107 -0
- package/dist/dialog-symbol-index.js.map +1 -0
- package/dist/dialog-symbol-provider.d.ts +5 -0
- package/dist/dialog-symbol-provider.d.ts.map +1 -0
- package/dist/dialog-symbol-provider.js +56 -0
- package/dist/dialog-symbol-provider.js.map +1 -0
- package/dist/dialog-web-export.d.ts +25 -0
- package/dist/dialog-web-export.d.ts.map +1 -0
- package/dist/dialog-web-export.js +283 -0
- package/dist/dialog-web-export.js.map +1 -0
- package/dist/dialog-workspace-symbol-provider.d.ts +16 -0
- package/dist/dialog-workspace-symbol-provider.d.ts.map +1 -0
- package/dist/dialog-workspace-symbol-provider.js +107 -0
- package/dist/dialog-workspace-symbol-provider.js.map +1 -0
- package/dist/dialog-workspace-watcher.d.ts +22 -0
- package/dist/dialog-workspace-watcher.d.ts.map +1 -0
- package/dist/dialog-workspace-watcher.js +102 -0
- package/dist/dialog-workspace-watcher.js.map +1 -0
- package/dist/dialoged/skein/compile-error.d.ts +10 -0
- package/dist/dialoged/skein/compile-error.d.ts.map +1 -0
- package/dist/dialoged/skein/compile-error.js +28 -0
- package/dist/dialoged/skein/compile-error.js.map +1 -0
- package/dist/dialoged/skein/dynamic.d.ts +14 -0
- package/dist/dialoged/skein/dynamic.d.ts.map +1 -0
- package/dist/dialoged/skein/dynamic.js +176 -0
- package/dist/dialoged/skein/dynamic.js.map +1 -0
- package/dist/dialoged/skein/frotz-build.d.ts +10 -0
- package/dist/dialoged/skein/frotz-build.d.ts.map +1 -0
- package/dist/dialoged/skein/frotz-build.js +70 -0
- package/dist/dialoged/skein/frotz-build.js.map +1 -0
- package/dist/dialoged/skein/index.d.ts +15 -0
- package/dist/dialoged/skein/index.d.ts.map +1 -0
- package/dist/dialoged/skein/index.js +31 -0
- package/dist/dialoged/skein/index.js.map +1 -0
- package/dist/dialoged/skein/io.d.ts +15 -0
- package/dist/dialoged/skein/io.d.ts.map +1 -0
- package/dist/dialoged/skein/io.js +53 -0
- package/dist/dialoged/skein/io.js.map +1 -0
- package/dist/dialoged/skein/persistence.d.ts +13 -0
- package/dist/dialoged/skein/persistence.d.ts.map +1 -0
- package/dist/dialoged/skein/persistence.js +228 -0
- package/dist/dialoged/skein/persistence.js.map +1 -0
- package/dist/dialoged/skein/process.d.ts +38 -0
- package/dist/dialoged/skein/process.d.ts.map +1 -0
- package/dist/dialoged/skein/process.js +189 -0
- package/dist/dialoged/skein/process.js.map +1 -0
- package/dist/dialoged/skein/progress.d.ts +17 -0
- package/dist/dialoged/skein/progress.d.ts.map +1 -0
- package/dist/dialoged/skein/progress.js +9 -0
- package/dist/dialoged/skein/progress.js.map +1 -0
- package/dist/dialoged/skein/project.d.ts +44 -0
- package/dist/dialoged/skein/project.d.ts.map +1 -0
- package/dist/dialoged/skein/project.js +223 -0
- package/dist/dialoged/skein/project.js.map +1 -0
- package/dist/dialoged/skein/search.d.ts +15 -0
- package/dist/dialoged/skein/search.d.ts.map +1 -0
- package/dist/dialoged/skein/search.js +99 -0
- package/dist/dialoged/skein/search.js.map +1 -0
- package/dist/dialoged/skein/service.d.ts +91 -0
- package/dist/dialoged/skein/service.d.ts.map +1 -0
- package/dist/dialoged/skein/service.js +1097 -0
- package/dist/dialoged/skein/service.js.map +1 -0
- package/dist/dialoged/skein/session.d.ts +96 -0
- package/dist/dialoged/skein/session.d.ts.map +1 -0
- package/dist/dialoged/skein/session.js +690 -0
- package/dist/dialoged/skein/session.js.map +1 -0
- package/dist/dialoged/skein/syntax.d.ts +5 -0
- package/dist/dialoged/skein/syntax.d.ts.map +1 -0
- package/dist/dialoged/skein/syntax.js +150 -0
- package/dist/dialoged/skein/syntax.js.map +1 -0
- package/dist/dialoged/skein/trace.d.ts +31 -0
- package/dist/dialoged/skein/trace.d.ts.map +1 -0
- package/dist/dialoged/skein/trace.js +172 -0
- package/dist/dialoged/skein/trace.js.map +1 -0
- package/dist/dialoged/skein/tree.d.ts +114 -0
- package/dist/dialoged/skein/tree.d.ts.map +1 -0
- package/dist/dialoged/skein/tree.js +701 -0
- package/dist/dialoged/skein/tree.js.map +1 -0
- package/dist/dialoged/skein/ui/ansi.d.ts +4 -0
- package/dist/dialoged/skein/ui/ansi.d.ts.map +1 -0
- package/dist/dialoged/skein/ui/ansi.js +215 -0
- package/dist/dialoged/skein/ui/ansi.js.map +1 -0
- package/dist/dialoged/skein/ui/diff.d.ts +7 -0
- package/dist/dialoged/skein/ui/diff.d.ts.map +1 -0
- package/dist/dialoged/skein/ui/diff.js +17 -0
- package/dist/dialoged/skein/ui/diff.js.map +1 -0
- package/dist/dialoged/skein/ui/knot-menu.d.ts +5 -0
- package/dist/dialoged/skein/ui/knot-menu.d.ts.map +1 -0
- package/dist/dialoged/skein/ui/knot-menu.js +89 -0
- package/dist/dialoged/skein/ui/knot-menu.js.map +1 -0
- package/dist/dialoged/skein/ui/marker-colors.d.ts +5 -0
- package/dist/dialoged/skein/ui/marker-colors.d.ts.map +1 -0
- package/dist/dialoged/skein/ui/marker-colors.js +18 -0
- package/dist/dialoged/skein/ui/marker-colors.js.map +1 -0
- package/dist/dialoged/skein/ui/render.d.ts +13 -0
- package/dist/dialoged/skein/ui/render.d.ts.map +1 -0
- package/dist/dialoged/skein/ui/render.js +332 -0
- package/dist/dialoged/skein/ui/render.js.map +1 -0
- package/dist/dialoged/skein/ui/traceRender.d.ts +11 -0
- package/dist/dialoged/skein/ui/traceRender.d.ts.map +1 -0
- package/dist/dialoged/skein/ui/traceRender.js +139 -0
- package/dist/dialoged/skein/ui/traceRender.js.map +1 -0
- package/dist/dialoged/skein/ui/tree-pane.d.ts +3 -0
- package/dist/dialoged/skein/ui/tree-pane.d.ts.map +1 -0
- package/dist/dialoged/skein/ui/tree-pane.js +135 -0
- package/dist/dialoged/skein/ui/tree-pane.js.map +1 -0
- package/dist/dialoged/skein/ui/webExportPage.d.ts +29 -0
- package/dist/dialoged/skein/ui/webExportPage.d.ts.map +1 -0
- package/dist/dialoged/skein/ui/webExportPage.js +72 -0
- package/dist/dialoged/skein/ui/webExportPage.js.map +1 -0
- package/dist/extension.d.ts +4 -0
- package/dist/extension.d.ts.map +1 -0
- package/dist/extension.js +885 -0
- package/dist/extension.js.map +1 -0
- package/dist/session-runner.d.ts +23 -0
- package/dist/session-runner.d.ts.map +1 -0
- package/dist/session-runner.js +92 -0
- package/dist/session-runner.js.map +1 -0
- package/language-configuration.json +43 -0
- package/media/icon.png +0 -0
- package/media/icons/arrow-down-circle-solid.svg +3 -0
- package/media/icons/arrow-down-solid.svg +3 -0
- package/media/icons/arrow-path-solid.svg +3 -0
- package/media/icons/arrow-right-solid.svg +3 -0
- package/media/icons/arrow-up-on-square-stack-solid.svg +5 -0
- package/media/icons/arrow-up-solid.svg +3 -0
- package/media/icons/arrow-uturn-left.svg +3 -0
- package/media/icons/arrow-uturn-right.svg +3 -0
- package/media/icons/bookmark-solid.svg +3 -0
- package/media/icons/bug-ant-solid.svg +3 -0
- package/media/icons/check-badge-solid.svg +3 -0
- package/media/icons/check-circle-solid.svg +3 -0
- package/media/icons/chevron-double-down-solid.svg +4 -0
- package/media/icons/chevron-double-up-solid.svg +4 -0
- package/media/icons/command-line-solid.svg +3 -0
- package/media/icons/cpu-chip-solid.svg +4 -0
- package/media/icons/dg-file-dark.svg +6 -0
- package/media/icons/dg-file-light.svg +6 -0
- package/media/icons/ellipsis-vertical-solid.svg +3 -0
- package/media/icons/exclamation-triangle.svg +3 -0
- package/media/icons/folder-arrow-down-solid.svg +3 -0
- package/media/icons/globe-alt.svg +3 -0
- package/media/icons/lock-closed-solid.svg +3 -0
- package/media/icons/magnifying-glass-solid.svg +3 -0
- package/media/icons/pencil-solid.svg +4 -0
- package/media/icons/play-solid.svg +3 -0
- package/media/icons/plus-circle-solid.svg +3 -0
- package/media/icons/scissors-solid.svg +5 -0
- package/media/icons/tag-solid.svg +3 -0
- package/media/icons/trash-solid.svg +3 -0
- package/media/icons/x-circle-solid.svg +3 -0
- package/media/js/datastar.js +9 -0
- package/media/js/main.js +1109 -0
- package/media/js/skein-loader.js +10 -0
- package/media/js/trace-loader.js +4 -0
- package/media/js/trace.js +147 -0
- package/media/style.css +5783 -0
- package/package.json +231 -0
- package/resources/bundle/default-cover.png +0 -0
- package/resources/bundle/introduction-to-if.pdf +0 -0
- package/resources/bundle/play-if-card.pdf +1990 -1
- package/resources/bundle/play.css +432 -0
- package/resources/bundle/style.css +450 -0
- package/resources/dfrotz-skein-patch.dg +4 -0
- package/syntaxes/dialog.tmLanguage.json +490 -0
|
@@ -0,0 +1,885 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.activate = activate;
|
|
37
|
+
exports.deactivate = deactivate;
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const vscode = __importStar(require("vscode"));
|
|
40
|
+
const skein_1 = require("./dialoged/skein");
|
|
41
|
+
const dialog_symbol_provider_1 = require("./dialog-symbol-provider");
|
|
42
|
+
const dialog_workspace_watcher_1 = require("./dialog-workspace-watcher");
|
|
43
|
+
const dialog_workspace_symbol_provider_1 = require("./dialog-workspace-symbol-provider");
|
|
44
|
+
const dialog_source_decoration_provider_1 = require("./dialog-source-decoration-provider");
|
|
45
|
+
const dialog_source_coverage_1 = require("./dialog-source-coverage");
|
|
46
|
+
const dialog_project_init_1 = require("./dialog-project-init");
|
|
47
|
+
const dialog_export_1 = require("./dialog-export");
|
|
48
|
+
const dialog_web_export_1 = require("./dialog-web-export");
|
|
49
|
+
const dialog_feelies_1 = require("./dialog-feelies");
|
|
50
|
+
const session_runner_1 = require("./session-runner");
|
|
51
|
+
const EXPORT_FORMATS = ['zblorb', 'z8', 'aa'];
|
|
52
|
+
let skeinService;
|
|
53
|
+
let skeinPanel;
|
|
54
|
+
let statusBarItem;
|
|
55
|
+
let activeSession;
|
|
56
|
+
let activeSessionId;
|
|
57
|
+
let activeProjectRoot;
|
|
58
|
+
let suppressReloadPromptUntil = 0;
|
|
59
|
+
let testTerminal;
|
|
60
|
+
let bundledBinDir;
|
|
61
|
+
let bundledLibraryDir;
|
|
62
|
+
let patchSourcePath;
|
|
63
|
+
let deactivating = false;
|
|
64
|
+
let compileErrorDiagnostics;
|
|
65
|
+
function clearCompileErrorDiagnostics() {
|
|
66
|
+
compileErrorDiagnostics?.clear();
|
|
67
|
+
}
|
|
68
|
+
async function isEngineAvailable(engine, binDir) {
|
|
69
|
+
return engine === 'dgdebug' ? (0, session_runner_1.isDgdebugAvailable)(binDir, bundledBinDir) : (0, session_runner_1.isDialogcAvailable)(binDir, bundledBinDir);
|
|
70
|
+
}
|
|
71
|
+
function engineUnavailableMessage(engine) {
|
|
72
|
+
const command = engine === 'dgdebug' ? 'dgdebug' : 'dialogc';
|
|
73
|
+
return `${command} was not found on PATH, in binDir, or bundled with this extension. Install the Dialog toolchain or set "binDir" in dialog.json.`;
|
|
74
|
+
}
|
|
75
|
+
async function activate(context) {
|
|
76
|
+
bundledBinDir = (0, skein_1.resolveBundledBinDir)(context.extensionPath);
|
|
77
|
+
bundledLibraryDir = (0, skein_1.resolveBundledLibraryDir)(context.extensionPath);
|
|
78
|
+
patchSourcePath = path.join(context.extensionPath, 'resources', 'dfrotz-skein-patch.dg');
|
|
79
|
+
compileErrorDiagnostics = vscode.languages.createDiagnosticCollection('dialog-compile-error');
|
|
80
|
+
context.subscriptions.push(compileErrorDiagnostics);
|
|
81
|
+
skeinService = new skein_1.SkeinService({
|
|
82
|
+
port: 0,
|
|
83
|
+
host: 'localhost',
|
|
84
|
+
mediaRoot: path.join(context.extensionPath, 'media'),
|
|
85
|
+
grammarPath: resolveDialogGrammarPath(context),
|
|
86
|
+
onCompileError: (error) => {
|
|
87
|
+
handleCompileError(error).catch((handlerError) => {
|
|
88
|
+
console.error('Failed to report compile error:', handlerError);
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
onTraceRequested: () => {
|
|
92
|
+
vscode.commands.executeCommand('dialogIdeTraceView.focus').then(undefined, (error) => {
|
|
93
|
+
console.error('Failed to reveal the Trace view:', error);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
await skeinService.start();
|
|
98
|
+
statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100);
|
|
99
|
+
refreshStatusBar();
|
|
100
|
+
statusBarItem.show();
|
|
101
|
+
context.subscriptions.push(statusBarItem);
|
|
102
|
+
context.subscriptions.push(vscode.window.registerWebviewViewProvider('dialogIdeTraceView', new TraceViewProvider(), {
|
|
103
|
+
webviewOptions: { retainContextWhenHidden: true }
|
|
104
|
+
}));
|
|
105
|
+
context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider({ language: 'dialog' }, new dialog_symbol_provider_1.DialogDocumentSymbolProvider()));
|
|
106
|
+
const workspaceWatcher = new dialog_workspace_watcher_1.DialogWorkspaceWatcher(getWorkspaceRoot());
|
|
107
|
+
const workspaceSymbolProvider = new dialog_workspace_symbol_provider_1.DialogWorkspaceSymbolProvider(workspaceWatcher);
|
|
108
|
+
context.subscriptions.push(workspaceWatcher, workspaceSymbolProvider, vscode.languages.registerWorkspaceSymbolProvider(workspaceSymbolProvider));
|
|
109
|
+
const sourceDecorationProvider = new dialog_source_decoration_provider_1.DialogSourceDecorationProvider(getWorkspaceRoot);
|
|
110
|
+
context.subscriptions.push(sourceDecorationProvider, vscode.window.registerFileDecorationProvider(sourceDecorationProvider));
|
|
111
|
+
context.subscriptions.push(workspaceWatcher.onDidChangeFiles((event) => {
|
|
112
|
+
activeSession?.markSourcesChanged();
|
|
113
|
+
if (event.type === 'create' || event.type === 'delete') {
|
|
114
|
+
sourceDecorationProvider.refresh([event.filePath]);
|
|
115
|
+
}
|
|
116
|
+
if (event.type === 'create') {
|
|
117
|
+
warnIfUncoveredSource(getWorkspaceRoot(), event.filePath).catch((error) => {
|
|
118
|
+
console.error('Failed to check dialog.json source coverage:', error);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}), workspaceWatcher.onDidChangeProjectConfig(() => {
|
|
122
|
+
activeSession?.markSourcesChanged();
|
|
123
|
+
sourceDecorationProvider.refresh(workspaceWatcher.listFiles());
|
|
124
|
+
warnIfDuplicateSources(getWorkspaceRoot()).catch((error) => {
|
|
125
|
+
console.error('Failed to check dialog.json for duplicate sources:', error);
|
|
126
|
+
});
|
|
127
|
+
}));
|
|
128
|
+
const workspaceRoot = getWorkspaceRoot();
|
|
129
|
+
if (workspaceRoot) {
|
|
130
|
+
const skeinFileWatcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(workspaceRoot, '**/*.skein'));
|
|
131
|
+
context.subscriptions.push(skeinFileWatcher, skeinFileWatcher.onDidChange(handleSkeinFileChangedOnDisk));
|
|
132
|
+
}
|
|
133
|
+
context.subscriptions.push(vscode.commands.registerCommand('dialog-ide.openSkein', () => ensureSkeinPanel()), vscode.commands.registerCommand('dialog-ide.runDefaultSkein', withErrorHandling(() => runDefaultSkein((0, session_runner_1.resolveProjectRoot)(getWorkspaceRoot())))), vscode.commands.registerCommand('dialog-ide.runSkein', withErrorHandling(() => runSkeinPicker((0, session_runner_1.resolveProjectRoot)(getWorkspaceRoot())))), vscode.commands.registerCommand('dialog-ide.newSkein', withErrorHandling(() => newSkeinSession((0, session_runner_1.resolveProjectRoot)(getWorkspaceRoot())))), vscode.commands.registerCommand('dialog-ide.stopSkein', withErrorHandling(stopSkeinCommand)), vscode.commands.registerCommand('dialog-ide.saveSkein', withErrorHandling(saveActiveSession)), vscode.commands.registerCommand('dialog-ide.debugInTerminal', withErrorHandling(() => debugInTerminal((0, session_runner_1.resolveProjectRoot)(getWorkspaceRoot())))), vscode.commands.registerCommand('dialog-ide.runTests', withErrorHandling(() => runTestsInTerminal((0, session_runner_1.resolveProjectRoot)(getWorkspaceRoot())))), vscode.commands.registerCommand('dialog-ide.addSourceToProject', withErrorHandling((filePath) => addSourceToProject((0, session_runner_1.resolveProjectRoot)(getWorkspaceRoot()), filePath ?? activeEditorDgFilePath()))), vscode.commands.registerCommand('dialog-ide.initProject', withErrorHandling(() => initDialogProject((0, session_runner_1.resolveProjectRoot)(getWorkspaceRoot()), bundledLibraryDir, path.join(context.extensionPath, 'resources', 'bundle', 'default-cover.png'), path.join(context.extensionPath, 'resources', 'bundle')))), vscode.commands.registerCommand('dialog-ide.configureExports', withErrorHandling(() => configureExports((0, session_runner_1.resolveProjectRoot)(getWorkspaceRoot())))), vscode.commands.registerCommand('dialog-ide.exportProject', withErrorHandling(() => exportDialogProject((0, session_runner_1.resolveProjectRoot)(getWorkspaceRoot())))), vscode.commands.registerCommand('dialog-ide.exportWebPage', withErrorHandling(() => exportWebPage((0, session_runner_1.resolveProjectRoot)(getWorkspaceRoot()), path.join(context.extensionPath, 'resources', 'bundle')))), vscode.commands.registerCommand('dialog-ide.addFeelie', withErrorHandling((uri) => addFeelieWizard((0, session_runner_1.resolveProjectRoot)(getWorkspaceRoot()), uri?.fsPath))), vscode.commands.registerCommand('dialog-ide.removeFeelie', withErrorHandling(() => removeFeelieWizard((0, session_runner_1.resolveProjectRoot)(getWorkspaceRoot())))));
|
|
134
|
+
warnAboutConflictingLanguageExtension(context);
|
|
135
|
+
}
|
|
136
|
+
async function deactivate() {
|
|
137
|
+
deactivating = true;
|
|
138
|
+
await stopActiveSession();
|
|
139
|
+
await skeinService?.stop();
|
|
140
|
+
skeinService = undefined;
|
|
141
|
+
}
|
|
142
|
+
function withErrorHandling(action) {
|
|
143
|
+
return async (...args) => {
|
|
144
|
+
try {
|
|
145
|
+
await action(...args);
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
vscode.window.showErrorMessage(error instanceof Error ? error.message : String(error));
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function getWorkspaceRoot() {
|
|
153
|
+
return vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
|
|
154
|
+
}
|
|
155
|
+
async function runDefaultSkein(projectRoot) {
|
|
156
|
+
const manager = new skein_1.PersistenceManager(projectRoot);
|
|
157
|
+
if (!(await manager.sessionExists(session_runner_1.DEFAULT_SESSION_ID))) {
|
|
158
|
+
throw new Error(`${session_runner_1.DEFAULT_SESSION_ID}.skein does not exist - use "New Skein..." instead.`);
|
|
159
|
+
}
|
|
160
|
+
await runLoadedSession(projectRoot, session_runner_1.DEFAULT_SESSION_ID);
|
|
161
|
+
}
|
|
162
|
+
async function runSkeinPicker(projectRoot) {
|
|
163
|
+
const ids = await (0, session_runner_1.listSkeinFiles)(projectRoot);
|
|
164
|
+
if (ids.length === 0) {
|
|
165
|
+
throw new Error('No .skein files found in this project - use "New Skein..." to create one.');
|
|
166
|
+
}
|
|
167
|
+
const picked = await vscode.window.showQuickPick(ids.map((id) => `${id}.skein`), { placeHolder: 'Choose a skein file to run' });
|
|
168
|
+
if (!picked) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
await runLoadedSession(projectRoot, (0, session_runner_1.toSessionId)(picked));
|
|
172
|
+
}
|
|
173
|
+
async function runLoadedSession(projectRoot, sessionId) {
|
|
174
|
+
if (!(await confirmStopIfRunning())) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
await loadAndActivateSession(projectRoot, sessionId, 'Running');
|
|
178
|
+
}
|
|
179
|
+
async function loadAndActivateSession(projectRoot, sessionId, verb) {
|
|
180
|
+
const project = (0, skein_1.readProject)(projectRoot);
|
|
181
|
+
const manager = new skein_1.PersistenceManager(projectRoot);
|
|
182
|
+
const tree = await manager.loadSession(sessionId);
|
|
183
|
+
if (!(await isEngineAvailable(tree.getEngine(), project.binDir))) {
|
|
184
|
+
throw new Error(engineUnavailableMessage(tree.getEngine()));
|
|
185
|
+
}
|
|
186
|
+
const session = skein_1.SkeinSession.createLoaded(tree, (0, session_runner_1.sessionConfigFromTree)(tree, projectRoot, bundledBinDir, patchSourcePath), skeinService);
|
|
187
|
+
if (!(await runSessionStep(() => session.start()))) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
setActiveSession(session, sessionId, projectRoot);
|
|
191
|
+
vscode.window.showInformationMessage(`${verb} ${sessionId}.skein (${tree.getEngine()})`);
|
|
192
|
+
const activeKnotId = session.getTree().getActiveKnotId();
|
|
193
|
+
if (activeKnotId !== null && activeKnotId !== 0) {
|
|
194
|
+
await runSessionStep(() => session.replayAll());
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async function reloadActiveSessionFromDisk() {
|
|
198
|
+
if (!activeSession || !activeSessionId || !activeProjectRoot) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const sessionId = activeSessionId;
|
|
202
|
+
const projectRoot = activeProjectRoot;
|
|
203
|
+
await stopActiveSession();
|
|
204
|
+
await loadAndActivateSession(projectRoot, sessionId, 'Reloaded');
|
|
205
|
+
}
|
|
206
|
+
function handleSkeinFileChangedOnDisk(uri) {
|
|
207
|
+
if (!activeSession || !activeSessionId || !activeProjectRoot) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (Date.now() < suppressReloadPromptUntil) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (uri.fsPath !== path.join(activeProjectRoot, `${activeSessionId}.skein`)) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
vscode.window
|
|
217
|
+
.showWarningMessage(`"${activeSessionId}.skein" was changed on disk outside Dialog IDE. Reload it? This discards any unsaved changes in the running session.`, { modal: true }, 'Reload')
|
|
218
|
+
.then((choice) => {
|
|
219
|
+
if (choice === 'Reload') {
|
|
220
|
+
reloadActiveSessionFromDisk().catch((error) => {
|
|
221
|
+
vscode.window.showErrorMessage(`Failed to reload skein: ${error.message}`);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
async function newSkeinSession(projectRoot) {
|
|
227
|
+
if (!(await confirmStopIfRunning())) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const engineChoice = await pickEngine();
|
|
231
|
+
if (!engineChoice) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const seedInput = await vscode.window.showInputBox({
|
|
235
|
+
prompt: 'Random seed (leave blank for a random one)',
|
|
236
|
+
validateInput: (value) => {
|
|
237
|
+
if (value.trim() === '') {
|
|
238
|
+
return undefined;
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
(0, session_runner_1.parseSeed)(value);
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
return error.message;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
if (seedInput === undefined) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const seed = seedInput.trim() === '' ? (0, session_runner_1.randomSeed)() : (0, session_runner_1.parseSeed)(seedInput);
|
|
253
|
+
const filenameInput = await vscode.window.showInputBox({
|
|
254
|
+
prompt: 'Skein file name',
|
|
255
|
+
value: `${session_runner_1.DEFAULT_SESSION_ID}.skein`,
|
|
256
|
+
validateInput: (value) => ((0, session_runner_1.isValidSessionId)((0, session_runner_1.toSessionId)(value)) ? undefined : 'Enter a plain file name, not a path.')
|
|
257
|
+
});
|
|
258
|
+
if (filenameInput === undefined) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const sessionId = (0, session_runner_1.toSessionId)(filenameInput);
|
|
262
|
+
const manager = new skein_1.PersistenceManager(projectRoot);
|
|
263
|
+
if (await manager.sessionExists(sessionId)) {
|
|
264
|
+
throw new Error(`${sessionId}.skein already exists - use "Run Skein..." to open it instead.`);
|
|
265
|
+
}
|
|
266
|
+
const project = (0, skein_1.readProject)(projectRoot);
|
|
267
|
+
if (!(await isEngineAvailable(engineChoice.engine, project.binDir))) {
|
|
268
|
+
throw new Error(engineUnavailableMessage(engineChoice.engine));
|
|
269
|
+
}
|
|
270
|
+
const session = skein_1.SkeinSession.createNew({ engine: engineChoice.engine, seed, projectRoot, bundledBinDir, patchSourcePath }, skeinService);
|
|
271
|
+
if (!(await runSessionStep(() => session.start()))) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
await manager.saveSession(session.getTree(), sessionId);
|
|
275
|
+
setActiveSession(session, sessionId, projectRoot);
|
|
276
|
+
vscode.window.showInformationMessage(`Started ${sessionId}.skein (${engineChoice.engine}, seed ${seed})`);
|
|
277
|
+
}
|
|
278
|
+
async function pickEngine() {
|
|
279
|
+
for (;;) {
|
|
280
|
+
const picked = await vscode.window.showQuickPick(session_runner_1.ENGINE_CHOICES.map((choice) => ({
|
|
281
|
+
label: choice.label,
|
|
282
|
+
description: choice.supported ? undefined : 'not yet implemented',
|
|
283
|
+
choice
|
|
284
|
+
})), { placeHolder: 'Choose an engine' });
|
|
285
|
+
if (!picked) {
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
if (!picked.choice.supported) {
|
|
289
|
+
vscode.window.showInformationMessage(`${picked.choice.label} isn't implemented yet - choose dgdebug.`);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
return picked.choice;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
async function stopSkeinCommand() {
|
|
296
|
+
if (!activeSession) {
|
|
297
|
+
vscode.window.showInformationMessage('No skein session is running.');
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
await stopActiveSession();
|
|
301
|
+
vscode.window.showInformationMessage('Skein session stopped.');
|
|
302
|
+
}
|
|
303
|
+
async function saveActiveSession() {
|
|
304
|
+
if (!activeSession || !activeSessionId || !activeProjectRoot) {
|
|
305
|
+
vscode.window.showInformationMessage('No skein session is running.');
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
suppressReloadPromptUntil = Date.now() + 2000;
|
|
309
|
+
await new skein_1.PersistenceManager(activeProjectRoot).saveSession(activeSession.getTree(), activeSessionId);
|
|
310
|
+
vscode.window.showInformationMessage(`Saved ${activeSessionId}.skein`);
|
|
311
|
+
}
|
|
312
|
+
async function debugInTerminal(projectRoot) {
|
|
313
|
+
const project = (0, skein_1.readProject)(projectRoot);
|
|
314
|
+
if (!(await (0, session_runner_1.isDgdebugAvailable)(project.binDir, bundledBinDir))) {
|
|
315
|
+
throw new Error('dgdebug was not found on PATH, in binDir, or bundled with this extension. Install the Dialog toolchain or set "binDir" in dialog.json.');
|
|
316
|
+
}
|
|
317
|
+
const sourceFiles = (0, skein_1.expandSources)(project, { debug: true, target: 'dgdebug' });
|
|
318
|
+
const terminal = vscode.window.createTerminal({
|
|
319
|
+
name: 'Dialog Debugger',
|
|
320
|
+
shellPath: (0, skein_1.resolveCommandPath)(project.binDir, 'dgdebug', bundledBinDir),
|
|
321
|
+
shellArgs: (0, session_runner_1.debugTerminalShellArgs)(sourceFiles)
|
|
322
|
+
});
|
|
323
|
+
terminal.show();
|
|
324
|
+
}
|
|
325
|
+
async function runTestsInTerminal(projectRoot) {
|
|
326
|
+
const project = (0, skein_1.readProject)(projectRoot);
|
|
327
|
+
if (!(await (0, session_runner_1.isDgdebugAvailable)(project.binDir, bundledBinDir))) {
|
|
328
|
+
throw new Error('dgdebug was not found on PATH, in binDir, or bundled with this extension. Install the Dialog toolchain or set "binDir" in dialog.json.');
|
|
329
|
+
}
|
|
330
|
+
testTerminal?.dispose();
|
|
331
|
+
const sourceFiles = (0, skein_1.expandSources)(project, { debug: true, test: true, target: 'dgdebug' });
|
|
332
|
+
testTerminal = vscode.window.createTerminal({
|
|
333
|
+
name: 'Dialog Tests',
|
|
334
|
+
shellPath: (0, skein_1.resolveCommandPath)(project.binDir, 'dgdebug', bundledBinDir),
|
|
335
|
+
shellArgs: (0, session_runner_1.testTerminalShellArgs)(sourceFiles)
|
|
336
|
+
});
|
|
337
|
+
testTerminal.show();
|
|
338
|
+
}
|
|
339
|
+
async function warnIfUncoveredSource(rootDir, filePath) {
|
|
340
|
+
if (!rootDir || !vscode.workspace.getConfiguration('dialog-ide').get('warnOnUncoveredSource', true)) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
let project;
|
|
344
|
+
try {
|
|
345
|
+
project = (0, skein_1.readProject)(rootDir);
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
if ((0, skein_1.isFileCoveredBySource)(project, filePath)) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const choice = await vscode.window.showWarningMessage(`${path.basename(filePath)} isn't part of any dialog.json source - it won't be compiled.`, 'Add to dialog.json');
|
|
354
|
+
if (choice === 'Add to dialog.json') {
|
|
355
|
+
await vscode.commands.executeCommand('dialog-ide.addSourceToProject', filePath);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
async function warnIfDuplicateSources(rootDir) {
|
|
359
|
+
if (!rootDir || !vscode.workspace.getConfiguration('dialog-ide').get('warnOnDuplicateSource', true)) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
let project;
|
|
363
|
+
try {
|
|
364
|
+
project = (0, skein_1.readProject)(rootDir);
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const duplicates = (0, skein_1.findDuplicateSourceFiles)(project);
|
|
370
|
+
if (duplicates.length === 0) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const summary = duplicates
|
|
374
|
+
.map(({ filePath, categories }) => `${path.basename(filePath)} (${categories.join(', ')})`)
|
|
375
|
+
.join(', ');
|
|
376
|
+
const choice = await vscode.window.showWarningMessage(`dialog.json declares the same source more than once: ${summary}. Dialog would compile it twice.`, 'Open dialog.json');
|
|
377
|
+
if (choice === 'Open dialog.json') {
|
|
378
|
+
const document = await vscode.workspace.openTextDocument(path.join(rootDir, 'dialog.json'));
|
|
379
|
+
await vscode.window.showTextDocument(document);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function activeEditorDgFilePath() {
|
|
383
|
+
const document = vscode.window.activeTextEditor?.document;
|
|
384
|
+
return document && document.fileName.endsWith('.dg') ? document.fileName : undefined;
|
|
385
|
+
}
|
|
386
|
+
async function addSourceToProject(projectRoot, filePath) {
|
|
387
|
+
if (!filePath) {
|
|
388
|
+
throw new Error('No .dg file to add - open one first, or use this from the "not covered by dialog.json" warning.');
|
|
389
|
+
}
|
|
390
|
+
const picked = await vscode.window.showQuickPick(dialog_source_coverage_1.SOURCE_CATEGORIES.map(({ category, description }) => ({ label: category, description, category })), { placeHolder: 'Add to which dialog.json source category?' });
|
|
391
|
+
if (!picked) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
const dialogJsonPath = path.join(projectRoot, 'dialog.json');
|
|
395
|
+
const relativePath = (0, dialog_source_coverage_1.toDialogJsonPath)(projectRoot, filePath);
|
|
396
|
+
(0, dialog_source_coverage_1.addSourceToDialogJson)(dialogJsonPath, picked.category, relativePath);
|
|
397
|
+
vscode.window.showInformationMessage(`Added ${relativePath} to dialog.json's "${picked.category}" sources.`);
|
|
398
|
+
}
|
|
399
|
+
async function initDialogProject(projectRoot, libraryDir, coverImageSource, pdfAssetsDir) {
|
|
400
|
+
const name = await vscode.window.showInputBox({
|
|
401
|
+
prompt: 'Dialog project name',
|
|
402
|
+
value: path.basename(projectRoot),
|
|
403
|
+
validateInput: (value) => (value.trim() === '' ? 'Enter a project name.' : undefined)
|
|
404
|
+
});
|
|
405
|
+
if (name === undefined) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
(0, dialog_project_init_1.scaffoldProject)(projectRoot, { name: name.trim() }, libraryDir, coverImageSource, pdfAssetsDir);
|
|
409
|
+
const storyDgPath = path.join(projectRoot, 'main', 'story.dg');
|
|
410
|
+
await vscode.window.showTextDocument(await vscode.workspace.openTextDocument(storyDgPath));
|
|
411
|
+
vscode.window.showInformationMessage(`Initialized Dialog project "${name.trim()}" in ${projectRoot}.`);
|
|
412
|
+
}
|
|
413
|
+
async function configureExports(projectRoot) {
|
|
414
|
+
const dialogJsonPath = path.join(projectRoot, 'dialog.json');
|
|
415
|
+
for (;;) {
|
|
416
|
+
const project = (0, skein_1.readProject)(projectRoot);
|
|
417
|
+
const items = [
|
|
418
|
+
{ label: '$(add) Add new export configuration...', action: 'add' },
|
|
419
|
+
...project.exports.map((config) => ({
|
|
420
|
+
label: config.name,
|
|
421
|
+
description: `${config.format}${config.includeDebug ? ', debug' : ''} → ${config.output}`,
|
|
422
|
+
action: 'remove',
|
|
423
|
+
name: config.name
|
|
424
|
+
})),
|
|
425
|
+
{
|
|
426
|
+
label: '$(gear) Set default dialogc options...',
|
|
427
|
+
description: project.dialogcOptions ? project.dialogcOptions.join(' ') : '(none)',
|
|
428
|
+
action: 'options'
|
|
429
|
+
},
|
|
430
|
+
{ label: '$(check) Done', action: 'done' }
|
|
431
|
+
];
|
|
432
|
+
const picked = await vscode.window.showQuickPick(items, {
|
|
433
|
+
placeHolder: 'Configure Dialog project exports'
|
|
434
|
+
});
|
|
435
|
+
if (!picked || picked.action === 'done') {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
if (picked.action === 'add') {
|
|
439
|
+
await addExportConfigWizard(dialogJsonPath, project);
|
|
440
|
+
}
|
|
441
|
+
else if (picked.action === 'options') {
|
|
442
|
+
await editProjectDialogcOptionsWizard(dialogJsonPath, project);
|
|
443
|
+
}
|
|
444
|
+
else if (picked.name) {
|
|
445
|
+
await removeExportConfigWizard(dialogJsonPath, picked.name);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
async function editProjectDialogcOptionsWizard(dialogJsonPath, project) {
|
|
450
|
+
const raw = await vscode.window.showInputBox({
|
|
451
|
+
prompt: 'Default dialogc options for this project (e.g. --heap 2000 --aux 1000) - leave blank to clear',
|
|
452
|
+
value: project.dialogcOptions ? project.dialogcOptions.join(' ') : ''
|
|
453
|
+
});
|
|
454
|
+
if (raw === undefined) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
(0, dialog_export_1.setProjectDialogcOptions)(dialogJsonPath, (0, dialog_export_1.parseDialogcOptionsInput)(raw));
|
|
458
|
+
vscode.window.showInformationMessage(raw.trim() === '' ? 'Cleared the project\'s default dialogc options.' : 'Updated the project\'s default dialogc options.');
|
|
459
|
+
}
|
|
460
|
+
async function addExportConfigWizard(dialogJsonPath, project) {
|
|
461
|
+
const name = await vscode.window.showInputBox({
|
|
462
|
+
prompt: 'Export configuration name',
|
|
463
|
+
validateInput: (value) => {
|
|
464
|
+
const trimmed = value.trim();
|
|
465
|
+
if (trimmed === '') {
|
|
466
|
+
return 'Enter a name.';
|
|
467
|
+
}
|
|
468
|
+
return project.exports.some((entry) => entry.name === trimmed) ? `"${trimmed}" already exists.` : undefined;
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
if (!name) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const formatChoice = await vscode.window.showQuickPick(EXPORT_FORMATS, { placeHolder: 'Output format' });
|
|
475
|
+
if (!formatChoice) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
const format = formatChoice;
|
|
479
|
+
const includeDebugChoice = await vscode.window.showQuickPick(['No', 'Yes'], {
|
|
480
|
+
placeHolder: 'Include debug sources? (usually No for a release build)'
|
|
481
|
+
});
|
|
482
|
+
if (!includeDebugChoice) {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
const draft = { name: name.trim(), format, includeDebug: includeDebugChoice === 'Yes', output: '' };
|
|
486
|
+
const output = await vscode.window.showInputBox({
|
|
487
|
+
prompt: 'Output path (relative to the project root)',
|
|
488
|
+
value: (0, dialog_export_1.defaultOutputPath)(draft)
|
|
489
|
+
});
|
|
490
|
+
if (!output) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const dialogcOptionsRaw = await vscode.window.showInputBox({
|
|
494
|
+
prompt: 'Extra dialogc options for this export (e.g. --heap 2000 --aux 1000) - leave blank to use the project\'s default, if any'
|
|
495
|
+
});
|
|
496
|
+
if (dialogcOptionsRaw === undefined) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const dialogcOptions = (0, dialog_export_1.parseDialogcOptionsInput)(dialogcOptionsRaw);
|
|
500
|
+
(0, dialog_export_1.addExportConfig)(dialogJsonPath, {
|
|
501
|
+
...draft,
|
|
502
|
+
output,
|
|
503
|
+
...(dialogcOptions.length > 0 ? { dialogcOptions } : {})
|
|
504
|
+
});
|
|
505
|
+
vscode.window.showInformationMessage(`Added export configuration "${draft.name}".`);
|
|
506
|
+
}
|
|
507
|
+
async function removeExportConfigWizard(dialogJsonPath, name) {
|
|
508
|
+
const choice = await vscode.window.showQuickPick(['Remove', 'Cancel'], {
|
|
509
|
+
placeHolder: `Remove export configuration "${name}"?`
|
|
510
|
+
});
|
|
511
|
+
if (choice !== 'Remove') {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
(0, dialog_export_1.removeExportConfig)(dialogJsonPath, name);
|
|
515
|
+
vscode.window.showInformationMessage(`Removed export configuration "${name}".`);
|
|
516
|
+
}
|
|
517
|
+
async function addFeelieWizard(projectRoot, filePath) {
|
|
518
|
+
if (!filePath) {
|
|
519
|
+
const picked = await vscode.window.showOpenDialog({
|
|
520
|
+
defaultUri: vscode.Uri.file(projectRoot),
|
|
521
|
+
canSelectMany: false,
|
|
522
|
+
openLabel: 'Add Feelie',
|
|
523
|
+
filters: { 'PDF files': ['pdf'], 'All files': ['*'] }
|
|
524
|
+
});
|
|
525
|
+
if (!picked || picked.length === 0) {
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
filePath = picked[0].fsPath;
|
|
529
|
+
}
|
|
530
|
+
const dialogJsonPath = path.join(projectRoot, 'dialog.json');
|
|
531
|
+
const feeliePath = (0, dialog_source_coverage_1.toDialogJsonPath)(projectRoot, filePath);
|
|
532
|
+
const name = await vscode.window.showInputBox({
|
|
533
|
+
prompt: 'Feelie display name (the link text on the exported page)',
|
|
534
|
+
value: path.basename(filePath, path.extname(filePath)),
|
|
535
|
+
validateInput: (value) => (value.trim() === '' ? 'Enter a name.' : undefined)
|
|
536
|
+
});
|
|
537
|
+
if (!name) {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
const feelie = { path: feeliePath, name: name.trim() };
|
|
541
|
+
(0, dialog_feelies_1.addFeelie)(dialogJsonPath, feelie);
|
|
542
|
+
vscode.window.showInformationMessage(`Added feelie "${feelie.name}".`);
|
|
543
|
+
}
|
|
544
|
+
async function removeFeelieWizard(projectRoot) {
|
|
545
|
+
const dialogJsonPath = path.join(projectRoot, 'dialog.json');
|
|
546
|
+
const project = (0, skein_1.readProject)(projectRoot);
|
|
547
|
+
const feelies = project.feelies ?? [];
|
|
548
|
+
if (feelies.length === 0) {
|
|
549
|
+
vscode.window.showInformationMessage('This project has no feelies configured.');
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
const picked = await vscode.window.showQuickPick(feelies.map((feelie) => ({ label: feelie.name, description: feelie.path, feelie })), { placeHolder: 'Remove which feelie?' });
|
|
553
|
+
if (!picked) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const choice = await vscode.window.showQuickPick(['Remove', 'Cancel'], {
|
|
557
|
+
placeHolder: `Remove feelie "${picked.feelie.name}"?`
|
|
558
|
+
});
|
|
559
|
+
if (choice !== 'Remove') {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
(0, dialog_feelies_1.removeFeelie)(dialogJsonPath, picked.feelie.path);
|
|
563
|
+
vscode.window.showInformationMessage(`Removed feelie "${picked.feelie.name}".`);
|
|
564
|
+
}
|
|
565
|
+
function revealFileLabel() {
|
|
566
|
+
return process.platform === 'darwin' ? 'Reveal in Finder' : 'Reveal in Explorer';
|
|
567
|
+
}
|
|
568
|
+
async function exportDialogProject(projectRoot) {
|
|
569
|
+
const project = (0, skein_1.readProject)(projectRoot);
|
|
570
|
+
if (project.exports.length === 0) {
|
|
571
|
+
const choice = await vscode.window.showErrorMessage('No export configurations defined yet - use "Configure Exports..." first.', 'Configure Exports...');
|
|
572
|
+
if (choice === 'Configure Exports...') {
|
|
573
|
+
await configureExports(projectRoot);
|
|
574
|
+
}
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const picked = await vscode.window.showQuickPick(project.exports.map((config) => ({
|
|
578
|
+
label: config.name,
|
|
579
|
+
description: `${config.format}${config.includeDebug ? ', debug' : ''} → ${config.output}`,
|
|
580
|
+
config
|
|
581
|
+
})), { placeHolder: 'Which export configuration?' });
|
|
582
|
+
if (!picked) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (!(await (0, session_runner_1.isDialogcAvailable)(project.binDir, bundledBinDir))) {
|
|
586
|
+
throw new Error('dialogc was not found on PATH, in binDir, or bundled with this extension. Install the Dialog toolchain or set "binDir" in dialog.json.');
|
|
587
|
+
}
|
|
588
|
+
const dialogcPath = (0, skein_1.resolveCommandPath)(project.binDir, 'dialogc', bundledBinDir);
|
|
589
|
+
const result = await (0, dialog_export_1.runDialogcExport)(project, picked.config, dialogcPath);
|
|
590
|
+
if (result.ok === true) {
|
|
591
|
+
const choice = await vscode.window.showInformationMessage(`Exported "${picked.config.name}" to ${result.outputPath}.`, revealFileLabel());
|
|
592
|
+
if (choice === revealFileLabel()) {
|
|
593
|
+
vscode.commands.executeCommand('revealFileInOS', vscode.Uri.file(result.outputPath));
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
const location = result.filePath && result.line ? ` (${result.filePath}, line ${result.line})` : '';
|
|
598
|
+
vscode.window.showErrorMessage(`Export failed${location}: ${result.message}`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
async function exportWebPage(projectRoot, assetsDir) {
|
|
602
|
+
const project = (0, skein_1.readProject)(projectRoot);
|
|
603
|
+
if (project.exports.length === 0) {
|
|
604
|
+
const choice = await vscode.window.showErrorMessage('No export configurations defined yet - use "Configure Exports..." first.', 'Configure Exports...');
|
|
605
|
+
if (choice === 'Configure Exports...') {
|
|
606
|
+
await configureExports(projectRoot);
|
|
607
|
+
}
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const picked = await vscode.window.showQuickPick(project.exports.map((config) => ({
|
|
611
|
+
label: config.name,
|
|
612
|
+
description: `${config.format}${config.includeDebug ? ', debug' : ''}`,
|
|
613
|
+
config
|
|
614
|
+
})), { placeHolder: 'Which export configuration builds the downloadable story file?' });
|
|
615
|
+
if (!picked) {
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
const [dialogcOk, dgdebugOk, aambundleOk] = await Promise.all([
|
|
619
|
+
(0, session_runner_1.isDialogcAvailable)(project.binDir, bundledBinDir),
|
|
620
|
+
(0, session_runner_1.isDgdebugAvailable)(project.binDir, bundledBinDir),
|
|
621
|
+
(0, session_runner_1.isAambundleAvailable)(project.binDir, bundledBinDir)
|
|
622
|
+
]);
|
|
623
|
+
const missing = [
|
|
624
|
+
!dialogcOk && 'dialogc',
|
|
625
|
+
!dgdebugOk && 'dgdebug',
|
|
626
|
+
!aambundleOk && 'aambundle'
|
|
627
|
+
].filter((name) => Boolean(name));
|
|
628
|
+
if (missing.length > 0) {
|
|
629
|
+
throw new Error(`${missing.join(', ')} not found on PATH, in binDir, or bundled with this extension. Install the Dialog toolchain (and AAmachine, for aambundle) or set "binDir" in dialog.json.`);
|
|
630
|
+
}
|
|
631
|
+
const paths = {
|
|
632
|
+
dialogcPath: (0, skein_1.resolveCommandPath)(project.binDir, 'dialogc', bundledBinDir),
|
|
633
|
+
aambundlePath: (0, skein_1.resolveCommandPath)(project.binDir, 'aambundle', bundledBinDir),
|
|
634
|
+
binDir: project.binDir,
|
|
635
|
+
bundledBinDir
|
|
636
|
+
};
|
|
637
|
+
const result = await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: `Exporting "${project.name}" as a web page...` }, () => (0, dialog_web_export_1.bundleWebExport)(project, picked.config, paths, assetsDir));
|
|
638
|
+
if (result.ok === true) {
|
|
639
|
+
const choice = await vscode.window.showInformationMessage(`Exported "${project.name}" to ${result.zipPath}.`, revealFileLabel());
|
|
640
|
+
if (choice === revealFileLabel()) {
|
|
641
|
+
vscode.commands.executeCommand('revealFileInOS', vscode.Uri.file(result.zipPath));
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
vscode.window.showErrorMessage(`Web page export failed (${result.step}): ${result.message}`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
async function confirmStopIfRunning() {
|
|
649
|
+
if (!activeSession) {
|
|
650
|
+
return true;
|
|
651
|
+
}
|
|
652
|
+
const choice = await vscode.window.showWarningMessage(`A skein session ("${activeSessionId}.skein") is already running. Stopping it discards any unsaved changes - save first if you want to keep them. Stop and continue?`, { modal: true }, 'Stop and Continue');
|
|
653
|
+
if (choice !== 'Stop and Continue') {
|
|
654
|
+
return false;
|
|
655
|
+
}
|
|
656
|
+
await stopActiveSession();
|
|
657
|
+
return true;
|
|
658
|
+
}
|
|
659
|
+
async function stopActiveSession() {
|
|
660
|
+
if (!activeSession) {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
activeSession.offChange(clearCompileErrorDiagnostics);
|
|
664
|
+
await activeSession.stop();
|
|
665
|
+
activeSession = undefined;
|
|
666
|
+
activeSessionId = undefined;
|
|
667
|
+
activeProjectRoot = undefined;
|
|
668
|
+
skeinService?.setActiveSession(undefined, undefined);
|
|
669
|
+
refreshStatusBar();
|
|
670
|
+
refreshSkeinPanel();
|
|
671
|
+
}
|
|
672
|
+
function setActiveSession(session, sessionId, projectRoot) {
|
|
673
|
+
activeSession = session;
|
|
674
|
+
activeSessionId = sessionId;
|
|
675
|
+
activeProjectRoot = projectRoot;
|
|
676
|
+
skeinService?.setActiveSession(session, sessionId, saveActiveSession);
|
|
677
|
+
session.onChange(clearCompileErrorDiagnostics);
|
|
678
|
+
refreshStatusBar();
|
|
679
|
+
ensureSkeinPanel();
|
|
680
|
+
refreshSkeinPanel();
|
|
681
|
+
}
|
|
682
|
+
async function runSessionStep(action) {
|
|
683
|
+
try {
|
|
684
|
+
await action();
|
|
685
|
+
return true;
|
|
686
|
+
}
|
|
687
|
+
catch (error) {
|
|
688
|
+
if (error instanceof skein_1.DialogCompileError) {
|
|
689
|
+
await handleCompileError(error);
|
|
690
|
+
return false;
|
|
691
|
+
}
|
|
692
|
+
throw error;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
async function handleCompileError(error) {
|
|
696
|
+
vscode.window.showErrorMessage(`Dialog compile error: ${error.message}`);
|
|
697
|
+
if (!error.filePath) {
|
|
698
|
+
compileErrorDiagnostics.clear();
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
const fullPath = path.isAbsolute(error.filePath)
|
|
702
|
+
? error.filePath
|
|
703
|
+
: path.join(activeProjectRoot ?? '', error.filePath);
|
|
704
|
+
try {
|
|
705
|
+
const document = await vscode.workspace.openTextDocument(fullPath);
|
|
706
|
+
const editor = await vscode.window.showTextDocument(document, { preview: false });
|
|
707
|
+
const lineIndex = Math.min(Math.max(0, (error.line ?? 1) - 1), document.lineCount - 1);
|
|
708
|
+
const range = document.lineAt(lineIndex).range;
|
|
709
|
+
editor.selection = new vscode.Selection(range.start, range.start);
|
|
710
|
+
editor.revealRange(range, vscode.TextEditorRevealType.InCenter);
|
|
711
|
+
const diagnostic = new vscode.Diagnostic(range, error.message, vscode.DiagnosticSeverity.Error);
|
|
712
|
+
diagnostic.source = 'dialog';
|
|
713
|
+
compileErrorDiagnostics.set(document.uri, [diagnostic]);
|
|
714
|
+
}
|
|
715
|
+
catch (openError) {
|
|
716
|
+
console.error('Failed to open compile-error source location:', openError);
|
|
717
|
+
compileErrorDiagnostics.clear();
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
function ensureSkeinPanel() {
|
|
721
|
+
if (skeinPanel) {
|
|
722
|
+
skeinPanel.reveal(vscode.ViewColumn.Beside);
|
|
723
|
+
return skeinPanel;
|
|
724
|
+
}
|
|
725
|
+
skeinPanel = vscode.window.createWebviewPanel('dialogIdeSkein', panelTitle(currentSessionDisplay()), vscode.ViewColumn.Beside, { enableScripts: true, retainContextWhenHidden: true });
|
|
726
|
+
skeinPanel.webview.html = getWebviewHtml(currentSessionDisplay());
|
|
727
|
+
skeinPanel.onDidDispose(() => {
|
|
728
|
+
skeinPanel = undefined;
|
|
729
|
+
stopActiveSession().catch((error) => {
|
|
730
|
+
vscode.window.showErrorMessage(error instanceof Error ? error.message : String(error));
|
|
731
|
+
});
|
|
732
|
+
});
|
|
733
|
+
return skeinPanel;
|
|
734
|
+
}
|
|
735
|
+
function refreshStatusBar() {
|
|
736
|
+
if (deactivating) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
if (activeSession && activeSessionId) {
|
|
740
|
+
const tree = activeSession.getTree();
|
|
741
|
+
statusBarItem.text = `$(debug-stop) Dialog Skein: ${activeSessionId}.skein (${tree.getEngine()})`;
|
|
742
|
+
statusBarItem.tooltip = `Seed ${tree.getSeed()} - click to open the Skein panel`;
|
|
743
|
+
statusBarItem.command = 'dialog-ide.openSkein';
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
statusBarItem.text = '$(play) Dialog Skein';
|
|
747
|
+
statusBarItem.tooltip = 'No skein session running - click to run the default skein';
|
|
748
|
+
statusBarItem.command = 'dialog-ide.runDefaultSkein';
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
function refreshSkeinPanel() {
|
|
752
|
+
if (deactivating || !skeinPanel) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
const display = currentSessionDisplay();
|
|
756
|
+
skeinPanel.title = panelTitle(display);
|
|
757
|
+
skeinPanel.webview.html = getWebviewHtml(display);
|
|
758
|
+
}
|
|
759
|
+
function currentSessionDisplay() {
|
|
760
|
+
if (!activeSession || !activeSessionId) {
|
|
761
|
+
return undefined;
|
|
762
|
+
}
|
|
763
|
+
const tree = activeSession.getTree();
|
|
764
|
+
return { sessionId: activeSessionId, engine: tree.getEngine(), seed: tree.getSeed() };
|
|
765
|
+
}
|
|
766
|
+
function panelTitle(active) {
|
|
767
|
+
return active ? `${active.sessionId}.skein` : 'Skein';
|
|
768
|
+
}
|
|
769
|
+
function getWebviewHtml(active) {
|
|
770
|
+
const serviceUrl = skeinService ? `http://localhost:${skeinService.getPort()}/` : undefined;
|
|
771
|
+
const body = serviceUrl
|
|
772
|
+
? `<iframe src="${serviceUrl}"></iframe>`
|
|
773
|
+
: `<p>Skein service is not running.</p>`;
|
|
774
|
+
const nonce = createNonce();
|
|
775
|
+
return `<!DOCTYPE html>
|
|
776
|
+
<html lang="en">
|
|
777
|
+
<head>
|
|
778
|
+
<meta charset="UTF-8" />
|
|
779
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'nonce-${nonce}'; frame-src http://localhost:*; child-src http://localhost:*;" />
|
|
780
|
+
<title>${panelTitle(active)}</title>
|
|
781
|
+
<style>
|
|
782
|
+
html, body { height: 100%; margin: 0; }
|
|
783
|
+
body { font-family: var(--vscode-font-family); color: var(--vscode-foreground); }
|
|
784
|
+
body:not(:has(iframe)) { padding: 1rem; }
|
|
785
|
+
iframe { display: block; width: 100%; height: 100%; border: 0; }
|
|
786
|
+
</style>
|
|
787
|
+
</head>
|
|
788
|
+
<body>
|
|
789
|
+
${body}
|
|
790
|
+
<script nonce="${nonce}">
|
|
791
|
+
window.addEventListener('message', (event) => {
|
|
792
|
+
if (event.data?.type !== 'forward-keydown') return;
|
|
793
|
+
const { key, code, metaKey, ctrlKey, shiftKey, altKey } = event.data;
|
|
794
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key, code, metaKey, ctrlKey, shiftKey, altKey, bubbles: true, cancelable: true }));
|
|
795
|
+
});
|
|
796
|
+
</script>
|
|
797
|
+
</body>
|
|
798
|
+
</html>`;
|
|
799
|
+
}
|
|
800
|
+
function resolveDialogGrammarPath(context) {
|
|
801
|
+
return path.join(context.extensionPath, 'syntaxes', 'dialog.tmLanguage.json');
|
|
802
|
+
}
|
|
803
|
+
const SIDEBURNS_EXTENSION_ID = 'sideburns3000.dialog-language-support';
|
|
804
|
+
const SIDEBURNS_WARNING_SHOWN_KEY = 'dialog-ide.sideburnsConflictWarningShown';
|
|
805
|
+
function warnAboutConflictingLanguageExtension(context) {
|
|
806
|
+
if (context.globalState.get(SIDEBURNS_WARNING_SHOWN_KEY)) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
if (!vscode.extensions.getExtension(SIDEBURNS_EXTENSION_ID)) {
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
context.globalState.update(SIDEBURNS_WARNING_SHOWN_KEY, true);
|
|
813
|
+
vscode.window
|
|
814
|
+
.showWarningMessage('Dialog IDE now provides its own .dg syntax highlighting, folding, and indentation. ' +
|
|
815
|
+
'Having the separate "Dialog Language Support" extension installed too can cause ' +
|
|
816
|
+
'inconsistent highlighting and a duplicate "Compile to..." menu - consider disabling or ' +
|
|
817
|
+
'uninstalling it.', 'Show Extension')
|
|
818
|
+
.then((choice) => {
|
|
819
|
+
if (choice === 'Show Extension') {
|
|
820
|
+
vscode.commands.executeCommand('workbench.extensions.search', `@id:${SIDEBURNS_EXTENSION_ID}`);
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
function createNonce() {
|
|
825
|
+
return Array.from({ length: 32 }, () => Math.floor(Math.random() * 16).toString(16)).join('');
|
|
826
|
+
}
|
|
827
|
+
async function openSourceAtLine(file, line) {
|
|
828
|
+
if (!activeProjectRoot) {
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
const fullPath = path.isAbsolute(file) ? file : path.join(activeProjectRoot, file);
|
|
832
|
+
const document = await vscode.workspace.openTextDocument(fullPath);
|
|
833
|
+
const editor = await vscode.window.showTextDocument(document, { preview: false });
|
|
834
|
+
const position = new vscode.Position(Math.max(0, line - 1), 0);
|
|
835
|
+
editor.selection = new vscode.Selection(position, position);
|
|
836
|
+
editor.revealRange(new vscode.Range(position, position), vscode.TextEditorRevealType.InCenter);
|
|
837
|
+
}
|
|
838
|
+
class TraceViewProvider {
|
|
839
|
+
resolveWebviewView(webviewView) {
|
|
840
|
+
webviewView.webview.options = { enableScripts: true };
|
|
841
|
+
webviewView.webview.html = getTraceWebviewHtml();
|
|
842
|
+
webviewView.webview.onDidReceiveMessage((message) => {
|
|
843
|
+
const msg = message;
|
|
844
|
+
if (msg?.type === 'openSource' && typeof msg.file === 'string' && typeof msg.line === 'number') {
|
|
845
|
+
openSourceAtLine(msg.file, msg.line).catch((error) => {
|
|
846
|
+
vscode.window.showErrorMessage(error instanceof Error ? error.message : String(error));
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
function getTraceWebviewHtml() {
|
|
853
|
+
const serviceUrl = skeinService ? `http://localhost:${skeinService.getPort()}/trace` : undefined;
|
|
854
|
+
const body = serviceUrl ? `<iframe src="${serviceUrl}"></iframe>` : `<p>Skein service is not running.</p>`;
|
|
855
|
+
const nonce = createNonce();
|
|
856
|
+
return `<!DOCTYPE html>
|
|
857
|
+
<html lang="en">
|
|
858
|
+
<head>
|
|
859
|
+
<meta charset="UTF-8" />
|
|
860
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'nonce-${nonce}'; frame-src http://localhost:*; child-src http://localhost:*;" />
|
|
861
|
+
<title>Trace</title>
|
|
862
|
+
<style>
|
|
863
|
+
html, body { height: 100%; margin: 0; }
|
|
864
|
+
body { font-family: var(--vscode-font-family); color: var(--vscode-foreground); }
|
|
865
|
+
body:not(:has(iframe)) { padding: 1rem; }
|
|
866
|
+
iframe { display: block; width: 100%; height: 100%; border: 0; }
|
|
867
|
+
</style>
|
|
868
|
+
</head>
|
|
869
|
+
<body>
|
|
870
|
+
${body}
|
|
871
|
+
<script nonce="${nonce}">
|
|
872
|
+
const vscodeApi = acquireVsCodeApi();
|
|
873
|
+
window.addEventListener('message', (event) => {
|
|
874
|
+
if (event.data?.type === 'forward-keydown') {
|
|
875
|
+
const { key, code, metaKey, ctrlKey, shiftKey, altKey } = event.data;
|
|
876
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key, code, metaKey, ctrlKey, shiftKey, altKey, bubbles: true, cancelable: true }));
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
vscodeApi.postMessage(event.data);
|
|
880
|
+
});
|
|
881
|
+
</script>
|
|
882
|
+
</body>
|
|
883
|
+
</html>`;
|
|
884
|
+
}
|
|
885
|
+
//# sourceMappingURL=extension.js.map
|