mellos-mapping 0.20.0 → 0.20.2
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/README.md +360 -63
- package/README.zh-CN.md +314 -54
- package/dist/hook-session-start.mjs +239 -0
- package/dist/mmap.mjs +338 -0
- package/dist/server.mjs +1614 -809
- package/dist/store-paths.mjs +107 -0
- package/dist/watch.mjs +1391 -760
- package/lib/domain/ops.d.ts +71 -12
- package/lib/domain/ops.js +145 -14
- package/lib/domain/types.d.ts +47 -6
- package/lib/domain/types.js +34 -3
- package/lib/render/canvas.d.ts +50 -0
- package/lib/render/canvas.js +210 -0
- package/lib/render/draw.d.ts +37 -0
- package/lib/render/draw.js +111 -0
- package/lib/render/layout.d.ts +89 -0
- package/lib/render/layout.js +200 -0
- package/lib/render/options.d.ts +39 -0
- package/lib/render/options.js +10 -0
- package/lib/render/render.d.ts +32 -46
- package/lib/render/render.js +58 -789
- package/lib/render/routing.d.ts +56 -0
- package/lib/render/routing.js +244 -0
- package/lib/render/skins.d.ts +54 -0
- package/lib/render/skins.js +99 -0
- package/lib/render/width.d.ts +24 -0
- package/lib/render/width.js +139 -0
- package/lib/render/zoom-geometry.d.ts +52 -0
- package/lib/render/zoom-geometry.js +56 -0
- package/lib/semantics/semantics.d.ts +53 -4
- package/lib/semantics/semantics.js +130 -6
- package/lib/semantics/vocabulary.d.ts +79 -0
- package/lib/semantics/vocabulary.js +112 -0
- package/lib/store/format.d.ts +17 -0
- package/lib/store/format.js +185 -66
- package/lib/store/store.d.ts +220 -20
- package/lib/store/store.js +491 -38
- package/package.json +12 -4
- package/scripts/codex-register.mjs +89 -20
- package/scripts/install-mmap-command.mjs +293 -0
- package/scripts/mmap.mjs +213 -0
- package/scripts/open-pane.mjs +115 -254
- package/scripts/pane-core.mjs +418 -0
package/scripts/open-pane.mjs
CHANGED
|
@@ -1,283 +1,144 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Open the live map pane in the RIGHT Windows Terminal window
|
|
3
|
+
* Open the live map pane in the RIGHT Windows Terminal window — the AGENT's
|
|
4
|
+
* launcher, addressed by `/mmap` and by the skill.
|
|
4
5
|
*
|
|
5
|
-
* node scripts/open-pane.mjs <project-dir> [--page <slug>] [--window] [--
|
|
6
|
+
* node scripts/open-pane.mjs <project-dir> [--page <slug>] [--window] [--force]
|
|
7
|
+
* [--ascii] [--no-color] [--no-mouse] [--no-follow]
|
|
8
|
+
* [--interval <ms>]
|
|
6
9
|
*
|
|
7
10
|
* Why this exists: the agent's shell runs on a hidden console (no WT_SESSION),
|
|
8
11
|
* so a bare `wt -w 0 sp` targets the MOST RECENTLY USED terminal window — with
|
|
9
12
|
* several windows open the map lands wherever the user last clicked, not
|
|
10
|
-
* beside the conversation.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* What does work — and what this script does, verifying every step:
|
|
16
|
-
*
|
|
17
|
-
* 1. Walk this process's ancestors and AttachConsole to each in turn; write
|
|
18
|
-
* a nonce into that console's title. The WT window whose title lights up
|
|
19
|
-
* is the one hosting this session. The nonce only shows when the session
|
|
20
|
-
* tab is the window's ACTIVE tab — exactly the precondition under which
|
|
21
|
-
* `sp` would split beside the conversation, so identification doubles as
|
|
22
|
-
* the go/no-go check.
|
|
23
|
-
* 2. Bring that window to the foreground (plain SetForegroundWindow, then
|
|
24
|
-
* the Alt-key unlock, then AttachThreadInput — Windows' foreground lock
|
|
25
|
-
* denies the plain call from background processes), verifying with
|
|
26
|
-
* GetForegroundWindow after each attempt.
|
|
27
|
-
* 3. Only then `wt -w 0 sp` — "most recently used" is now provably ours.
|
|
28
|
-
*
|
|
29
|
-
* Any step failing falls back to a dedicated window named "mellos-mapping":
|
|
30
|
-
* deterministic, never a random window. `--window` picks that mode outright
|
|
31
|
-
* (explicit choice for users who want the map separate from the chat).
|
|
32
|
-
*
|
|
33
|
-
* Known race, accepted: if the user focuses a DIFFERENT terminal window in
|
|
34
|
-
* the ~1s between our focus-verify and wt reading its MRU state, the split
|
|
35
|
-
* can still land there. The window is at least one the user is actively in.
|
|
13
|
+
* beside the conversation. The window probe that solves it, the check for an
|
|
14
|
+
* already-running watcher and the `wt` payload live in ./pane-core.mjs, which
|
|
15
|
+
* documents each of them; this file is the command line, the policy and the
|
|
16
|
+
* machine-readable report on top.
|
|
36
17
|
*
|
|
37
18
|
* --page <slug> opens the map ON that page (the effort under discussion, not
|
|
38
19
|
* whatever page the store lists first). With a watcher already running it
|
|
39
20
|
* writes the one-shot focus file instead — the existing pane retargets within
|
|
40
21
|
* a poll tick — so re-running with --page is also how you steer an open pane.
|
|
22
|
+
* Every other flag belongs to the WATCHER and is forwarded verbatim; an
|
|
23
|
+
* unknown flag is a usage error, never a silently dropped intention.
|
|
24
|
+
*
|
|
25
|
+
* What this launcher deliberately does NOT do is CLOSE a pane. An assistant
|
|
26
|
+
* asking for the map must not be able to take one away from the user; the
|
|
27
|
+
* toggle is the human's command, `mmap` (./mmap.mjs).
|
|
28
|
+
*
|
|
29
|
+
* Pure helpers are exported for the spec; the launcher runs only as an entry
|
|
30
|
+
* point, so importing this file is inert.
|
|
41
31
|
*/
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
|
|
45
|
-
import {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (positional.length !== 1) {
|
|
61
|
-
console.error(USAGE);
|
|
62
|
-
process.exit(1);
|
|
63
|
-
}
|
|
64
|
-
// Mirrors ID_RULE in src/domain/types.ts — this script runs standalone and
|
|
65
|
-
// cannot import the TypeScript sources.
|
|
66
|
-
if (pageSlug !== undefined && !/^[a-z0-9][a-z0-9-]{0,63}$/.test(pageSlug)) {
|
|
67
|
-
console.error(`--page needs a kebab-case slug (got "${pageSlug}")\n${USAGE}`);
|
|
68
|
-
process.exit(1);
|
|
69
|
-
}
|
|
70
|
-
if (process.platform !== 'win32') {
|
|
71
|
-
console.error('open-pane.mjs is Windows Terminal-only — use the tmux/manual route from the command doc.');
|
|
72
|
-
process.exit(1);
|
|
73
|
-
}
|
|
32
|
+
import { existsSync } from 'node:fs';
|
|
33
|
+
import { join, resolve } from 'node:path';
|
|
34
|
+
|
|
35
|
+
import {
|
|
36
|
+
DEDICATED_WINDOW_NAME,
|
|
37
|
+
PANE_MODE,
|
|
38
|
+
launchedAsEntry,
|
|
39
|
+
loadPluginPaths,
|
|
40
|
+
placePane,
|
|
41
|
+
pluginRootOf,
|
|
42
|
+
takeWatcherFlag,
|
|
43
|
+
paneIsOpen,
|
|
44
|
+
writeFocusRequest,
|
|
45
|
+
} from './pane-core.mjs';
|
|
46
|
+
|
|
47
|
+
export const USAGE =
|
|
48
|
+
'usage: node scripts/open-pane.mjs <project-dir> [--page <slug>] [--window] [--force]' +
|
|
49
|
+
' [--ascii] [--no-color] [--no-mouse] [--no-follow] [--interval <ms>]';
|
|
74
50
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
`Write-Output "WATCHERS=$($w.Count)"`,
|
|
109
|
-
);
|
|
110
|
-
const m = out.match(/WATCHERS=(\d+)/);
|
|
111
|
-
return m !== null && Number(m[1]) > 0;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Prints IDENT=<hwnd|0> and, when identified, FOCUS=<1|0>.
|
|
115
|
-
const IDENTIFY_AND_FOCUS = String.raw`
|
|
116
|
-
$ErrorActionPreference = 'SilentlyContinue'
|
|
117
|
-
Add-Type @"
|
|
118
|
-
using System;
|
|
119
|
-
using System.Text;
|
|
120
|
-
using System.Runtime.InteropServices;
|
|
121
|
-
public class MmapWin {
|
|
122
|
-
[DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc cb, IntPtr lp);
|
|
123
|
-
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lp);
|
|
124
|
-
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
|
|
125
|
-
[DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern int GetClassName(IntPtr hWnd, StringBuilder sb, int max);
|
|
126
|
-
[DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder sb, int max);
|
|
127
|
-
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
128
|
-
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
|
|
129
|
-
[DllImport("user32.dll")] public static extern bool BringWindowToTop(IntPtr hWnd);
|
|
130
|
-
[DllImport("user32.dll")] public static extern bool AttachThreadInput(uint a, uint b, bool f);
|
|
131
|
-
[DllImport("kernel32.dll")] public static extern uint GetCurrentThreadId();
|
|
132
|
-
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint pid);
|
|
133
|
-
[DllImport("user32.dll")] public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, IntPtr dwExtraInfo);
|
|
134
|
-
[DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
135
|
-
[DllImport("user32.dll")] public static extern bool IsIconic(IntPtr hWnd);
|
|
136
|
-
[DllImport("kernel32.dll")] public static extern bool FreeConsole();
|
|
137
|
-
[DllImport("kernel32.dll")] public static extern bool AttachConsole(uint pid);
|
|
138
|
-
[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern bool SetConsoleTitle(string title);
|
|
139
|
-
[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern uint GetConsoleTitle(StringBuilder sb, uint size);
|
|
140
|
-
}
|
|
141
|
-
"@
|
|
142
|
-
function Get-WtWindows {
|
|
143
|
-
$wins = New-Object System.Collections.ArrayList
|
|
144
|
-
$cb = {
|
|
145
|
-
param($h, $lp)
|
|
146
|
-
if ([MmapWin]::IsWindowVisible($h)) {
|
|
147
|
-
$cls = New-Object System.Text.StringBuilder 256
|
|
148
|
-
[void][MmapWin]::GetClassName($h, $cls, 256)
|
|
149
|
-
if ($cls.ToString() -eq 'CASCADIA_HOSTING_WINDOW_CLASS') {
|
|
150
|
-
$t = New-Object System.Text.StringBuilder 512
|
|
151
|
-
[void][MmapWin]::GetWindowText($h, $t, 512)
|
|
152
|
-
[void]$wins.Add(@{ hwnd = $h.ToInt64(); title = $t.ToString() })
|
|
153
|
-
}
|
|
51
|
+
/**
|
|
52
|
+
* Parse the launcher's command line.
|
|
53
|
+
*
|
|
54
|
+
* @param argv - arguments after the script path.
|
|
55
|
+
* @param idRule - the store's page-slug grammar (ID_RULE), passed in rather
|
|
56
|
+
* than restated, so this parser cannot drift from the ids the store accepts.
|
|
57
|
+
* @returns ok(config) or err(message) — a bad command line is an expected
|
|
58
|
+
* outcome of a hand-typed line, not an exception.
|
|
59
|
+
*/
|
|
60
|
+
export function parsePaneArgs(argv, idRule) {
|
|
61
|
+
const positional = [];
|
|
62
|
+
const watcherFlags = [];
|
|
63
|
+
let pageSlug;
|
|
64
|
+
let mode = PANE_MODE.split;
|
|
65
|
+
let force = false;
|
|
66
|
+
for (let i = 0; i < argv.length; i++) {
|
|
67
|
+
const a = argv[i];
|
|
68
|
+
const watcher = takeWatcherFlag(argv, i);
|
|
69
|
+
if (watcher.kind === 'bad-value') return { ok: false, error: `${watcher.message}\n${USAGE}` };
|
|
70
|
+
if (watcher.kind === 'taken') {
|
|
71
|
+
watcherFlags.push(...watcher.flags);
|
|
72
|
+
i = watcher.next;
|
|
73
|
+
} else if (a === '--page') {
|
|
74
|
+
pageSlug = argv[++i];
|
|
75
|
+
if (pageSlug === undefined) return { ok: false, error: `--page needs a slug\n${USAGE}` };
|
|
76
|
+
} else if (a === '--window') {
|
|
77
|
+
mode = PANE_MODE.window;
|
|
78
|
+
} else if (a === '--force') {
|
|
79
|
+
force = true;
|
|
80
|
+
} else if (a.startsWith('--')) {
|
|
81
|
+
return { ok: false, error: `unknown flag "${a}"\n${USAGE}` };
|
|
82
|
+
} else {
|
|
83
|
+
positional.push(a);
|
|
154
84
|
}
|
|
155
|
-
return $true
|
|
156
85
|
}
|
|
157
|
-
|
|
158
|
-
|
|
86
|
+
if (positional.length !== 1) return { ok: false, error: USAGE };
|
|
87
|
+
if (pageSlug !== undefined && !idRule.test(pageSlug)) {
|
|
88
|
+
return { ok: false, error: `--page needs a kebab-case slug (got "${pageSlug}")\n${USAGE}` };
|
|
89
|
+
}
|
|
90
|
+
return { ok: true, value: { projectDir: resolve(positional[0]), pageSlug, mode, force, watcherFlags } };
|
|
159
91
|
}
|
|
160
92
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
93
|
+
async function main() {
|
|
94
|
+
const loaded = await loadPluginPaths(pluginRootOf(import.meta.url));
|
|
95
|
+
if (!loaded.ok) {
|
|
96
|
+
console.error(loaded.error);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
const { store, watchPath } = loaded.value;
|
|
169
100
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
foreach ($w in (Get-WtWindows)) {
|
|
184
|
-
if ($w.title -like "*$nonce*") { $hwnd = [IntPtr]$w.hwnd; break }
|
|
185
|
-
}
|
|
101
|
+
const parsed = parsePaneArgs(process.argv.slice(2), store.ID_RULE);
|
|
102
|
+
if (!parsed.ok) {
|
|
103
|
+
console.error(parsed.error);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
const cfg = parsed.value;
|
|
107
|
+
if (!existsSync(cfg.projectDir)) {
|
|
108
|
+
console.error(`project directory does not exist: ${cfg.projectDir}`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
if (process.platform !== 'win32') {
|
|
112
|
+
console.error('open-pane.mjs is Windows Terminal-only — use the tmux/manual route from the command doc.');
|
|
113
|
+
process.exit(1);
|
|
186
114
|
}
|
|
187
|
-
Start-Sleep -Milliseconds 100
|
|
188
|
-
[void][MmapWin]::SetConsoleTitle($orig)
|
|
189
|
-
Start-Sleep -Milliseconds 200
|
|
190
|
-
[void][MmapWin]::FreeConsole()
|
|
191
|
-
if ($hwnd -ne [IntPtr]::Zero) { break }
|
|
192
|
-
}
|
|
193
115
|
|
|
194
|
-
|
|
195
|
-
Write-Output "IDENT=$($hwnd.ToInt64())"
|
|
116
|
+
const mapFile = join(cfg.projectDir, store.STATE_FILE_RELATIVE_PATH);
|
|
196
117
|
|
|
197
|
-
if (
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
if (-not $focused) {
|
|
210
|
-
$fgpid = 0
|
|
211
|
-
$fgThread = [MmapWin]::GetWindowThreadProcessId([MmapWin]::GetForegroundWindow(), [ref]$fgpid)
|
|
212
|
-
$myThread = [MmapWin]::GetCurrentThreadId()
|
|
213
|
-
[void][MmapWin]::AttachThreadInput($myThread, $fgThread, $true)
|
|
214
|
-
[void][MmapWin]::BringWindowToTop($hwnd)
|
|
215
|
-
[void][MmapWin]::SetForegroundWindow($hwnd)
|
|
216
|
-
[void][MmapWin]::AttachThreadInput($myThread, $fgThread, $false)
|
|
217
|
-
Start-Sleep -Milliseconds 150
|
|
218
|
-
if ([MmapWin]::GetForegroundWindow() -eq $hwnd) { $focused = $true }
|
|
219
|
-
}
|
|
220
|
-
Write-Output "FOCUS=$(if ($focused) { 1 } else { 0 })"
|
|
221
|
-
`;
|
|
222
|
-
|
|
223
|
-
function paneCommand() {
|
|
224
|
-
const cmd = ['--title', 'mellos map', '-d', projectDir, 'node', watchPath, '--file', mapFile];
|
|
225
|
-
if (flags.has('--ascii')) cmd.push('--ascii');
|
|
226
|
-
if (pageSlug !== undefined) cmd.push('--page', pageSlug);
|
|
227
|
-
return cmd;
|
|
228
|
-
}
|
|
118
|
+
if (!cfg.force && paneIsOpen(store, mapFile)) {
|
|
119
|
+
if (cfg.pageSlug !== undefined) {
|
|
120
|
+
writeFocusRequest(store.focusFilePath(mapFile), cfg.pageSlug);
|
|
121
|
+
console.log(`MMAP_PANE already-open refocused=${cfg.pageSlug}`);
|
|
122
|
+
console.log(`A watcher for ${mapFile} is already running — asked it to show page "${cfg.pageSlug}".`);
|
|
123
|
+
} else {
|
|
124
|
+
console.log('MMAP_PANE already-open');
|
|
125
|
+
console.log(`A watcher for ${mapFile} is already running — not opening another pane (use --force to override).`);
|
|
126
|
+
}
|
|
127
|
+
process.exit(0);
|
|
128
|
+
}
|
|
229
129
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
console.error(`wt failed to ${what} (exit ${r.status ?? 'timeout'}) — is Windows Terminal installed?`);
|
|
130
|
+
const placed = placePane(cfg, watchPath, mapFile);
|
|
131
|
+
if (!placed.ok) {
|
|
132
|
+
console.error(placed.error);
|
|
234
133
|
process.exit(1);
|
|
235
134
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
openWt(['-w', 'mellos-mapping', 'nt', ...paneCommand()], 'open the dedicated window');
|
|
240
|
-
console.log(`MMAP_PANE mode=window name=mellos-mapping reason=${reason}`);
|
|
241
|
-
console.log('Map opened in the dedicated "mellos-mapping" window.');
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (!flags.has('--force') && watcherAlreadyRunning()) {
|
|
245
|
-
if (pageSlug !== undefined) {
|
|
246
|
-
// One-shot focus request (see takeFocusRequest in src/store/store.ts):
|
|
247
|
-
// the running watcher consumes and deletes it within a poll tick. Temp +
|
|
248
|
-
// rename because the watcher polls: a torn read would be swept as junk,
|
|
249
|
-
// silently losing the request.
|
|
250
|
-
mkdirSync(dirname(mapFile), { recursive: true });
|
|
251
|
-
const focusFile = join(dirname(mapFile), 'mellos-mapping.focus');
|
|
252
|
-
writeFileSync(`${focusFile}.tmp`, JSON.stringify({ page: pageSlug }));
|
|
253
|
-
renameSync(`${focusFile}.tmp`, focusFile);
|
|
254
|
-
console.log(`MMAP_PANE already-open refocused=${pageSlug}`);
|
|
255
|
-
console.log(`A watcher for ${mapFile} is already running — asked it to show page "${pageSlug}".`);
|
|
135
|
+
if (placed.value.mode === PANE_MODE.window) {
|
|
136
|
+
console.log(`MMAP_PANE mode=window name=${DEDICATED_WINDOW_NAME} reason=${placed.value.reason}`);
|
|
137
|
+
console.log(`Map opened in the dedicated "${DEDICATED_WINDOW_NAME}" window.`);
|
|
256
138
|
} else {
|
|
257
|
-
console.log(
|
|
258
|
-
console.log(
|
|
139
|
+
console.log(`MMAP_PANE mode=split hwnd=${placed.value.hwnd}`);
|
|
140
|
+
console.log('Map opened beside this conversation (vertical split).');
|
|
259
141
|
}
|
|
260
|
-
process.exit(0);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (flags.has('--window')) {
|
|
264
|
-
openDedicatedWindow('requested');
|
|
265
|
-
process.exit(0);
|
|
266
142
|
}
|
|
267
143
|
|
|
268
|
-
|
|
269
|
-
const out = runPowerShell(IDENTIFY_AND_FOCUS.replaceAll('__NONCE__', nonce));
|
|
270
|
-
const ident = out.match(/IDENT=(\d+)/)?.[1] ?? '0';
|
|
271
|
-
const focused = /FOCUS=1/.test(out);
|
|
272
|
-
|
|
273
|
-
if (ident === '0') {
|
|
274
|
-
openDedicatedWindow('session-window-not-identified');
|
|
275
|
-
} else if (!focused) {
|
|
276
|
-
openDedicatedWindow('session-window-focus-denied');
|
|
277
|
-
} else {
|
|
278
|
-
// The identified window is foreground right now, so "most recently used"
|
|
279
|
-
// is deterministically it (see the race note in the header).
|
|
280
|
-
openWt(['-w', '0', 'sp', '-V', '--size', '0.42', ...paneCommand()], 'split the session window');
|
|
281
|
-
console.log(`MMAP_PANE mode=split hwnd=${ident}`);
|
|
282
|
-
console.log('Map opened beside this conversation (vertical split).');
|
|
283
|
-
}
|
|
144
|
+
if (launchedAsEntry(process.argv[1], import.meta.url)) await main();
|