@bastani/atomic 0.9.40-alpha.1 → 0.9.40
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 +37 -0
- package/dist/builtin/intercom/CHANGELOG.md +14 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +31 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/CHANGELOG.md +12 -0
- package/dist/builtin/web-access/README.md +1 -1
- package/dist/builtin/web-access/index.bundle.mjs +2 -5
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +25 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +11 -2
- package/dist/cli/args.js +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js +3 -2
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/template.html +1 -0
- package/dist/core/export-html/template.js +37 -20
- package/dist/core/export-html/vendor/marked-cjk-friendly.min.js +6 -0
- package/dist/core/export-html/vendor/marked.min.js +76 -3
- package/docs/providers.md +1 -1
- package/docs/settings.md +1 -1
- package/npm-shrinkwrap.json +35 -35
- package/package.json +5 -5
- package/dist/core/export-html/template-js/data-tree.js +0 -287
- package/dist/core/export-html/template-js/entries-navigation.js +0 -434
- package/dist/core/export-html/template-js/initialization.js +0 -335
- package/dist/core/export-html/template-js/message-tools.js +0 -428
- package/dist/core/export-html/template-js/tree-filter-render.js +0 -419
- package/dist/core/export-html/template-script.d.ts +0 -3
- package/dist/core/export-html/template-script.d.ts.map +0 -1
- package/dist/core/export-html/template-script.js +0 -13
- package/dist/core/export-html/template-script.js.map +0 -1
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/atomic",
|
|
3
|
-
"version": "0.9.40
|
|
3
|
+
"version": "0.9.40",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bastani/atomic",
|
|
9
|
-
"version": "0.9.40
|
|
9
|
+
"version": "0.9.40",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@bastani/atomic-natives": "0.9.40
|
|
13
|
-
"@bastani/pi-ai": "0.9.40
|
|
12
|
+
"@bastani/atomic-natives": "0.9.40",
|
|
13
|
+
"@bastani/pi-ai": "0.9.40",
|
|
14
14
|
"@earendil-works/pi-agent-core": "1.3.0",
|
|
15
15
|
"@earendil-works/pi-codemode": "1.3.0",
|
|
16
16
|
"@earendil-works/pi-tui": "1.3.0",
|
|
@@ -427,18 +427,18 @@
|
|
|
427
427
|
}
|
|
428
428
|
},
|
|
429
429
|
"node_modules/@bastani/atomic-natives": {
|
|
430
|
-
"version": "0.9.40
|
|
431
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives/-/atomic-natives-0.9.40
|
|
430
|
+
"version": "0.9.40",
|
|
431
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives/-/atomic-natives-0.9.40.tgz",
|
|
432
432
|
"license": "MIT",
|
|
433
433
|
"optionalDependencies": {
|
|
434
|
-
"@bastani/atomic-natives-darwin-arm64": "0.9.40
|
|
435
|
-
"@bastani/atomic-natives-darwin-x64": "0.9.40
|
|
436
|
-
"@bastani/atomic-natives-linux-arm64-gnu": "0.9.40
|
|
437
|
-
"@bastani/atomic-natives-linux-arm64-musl": "0.9.40
|
|
438
|
-
"@bastani/atomic-natives-linux-x64-gnu": "0.9.40
|
|
439
|
-
"@bastani/atomic-natives-linux-x64-musl": "0.9.40
|
|
440
|
-
"@bastani/atomic-natives-win32-arm64-msvc": "0.9.40
|
|
441
|
-
"@bastani/atomic-natives-win32-x64-msvc": "0.9.40
|
|
434
|
+
"@bastani/atomic-natives-darwin-arm64": "0.9.40",
|
|
435
|
+
"@bastani/atomic-natives-darwin-x64": "0.9.40",
|
|
436
|
+
"@bastani/atomic-natives-linux-arm64-gnu": "0.9.40",
|
|
437
|
+
"@bastani/atomic-natives-linux-arm64-musl": "0.9.40",
|
|
438
|
+
"@bastani/atomic-natives-linux-x64-gnu": "0.9.40",
|
|
439
|
+
"@bastani/atomic-natives-linux-x64-musl": "0.9.40",
|
|
440
|
+
"@bastani/atomic-natives-win32-arm64-msvc": "0.9.40",
|
|
441
|
+
"@bastani/atomic-natives-win32-x64-msvc": "0.9.40"
|
|
442
442
|
},
|
|
443
443
|
"engines": {
|
|
444
444
|
"bun": ">=1.4.2",
|
|
@@ -446,8 +446,8 @@
|
|
|
446
446
|
}
|
|
447
447
|
},
|
|
448
448
|
"node_modules/@bastani/atomic-natives-darwin-arm64": {
|
|
449
|
-
"version": "0.9.40
|
|
450
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-arm64/-/atomic-natives-darwin-arm64-0.9.40
|
|
449
|
+
"version": "0.9.40",
|
|
450
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-arm64/-/atomic-natives-darwin-arm64-0.9.40.tgz",
|
|
451
451
|
"license": "MIT",
|
|
452
452
|
"os": [
|
|
453
453
|
"darwin"
|
|
@@ -458,8 +458,8 @@
|
|
|
458
458
|
"optional": true
|
|
459
459
|
},
|
|
460
460
|
"node_modules/@bastani/atomic-natives-darwin-x64": {
|
|
461
|
-
"version": "0.9.40
|
|
462
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-x64/-/atomic-natives-darwin-x64-0.9.40
|
|
461
|
+
"version": "0.9.40",
|
|
462
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-x64/-/atomic-natives-darwin-x64-0.9.40.tgz",
|
|
463
463
|
"license": "MIT",
|
|
464
464
|
"os": [
|
|
465
465
|
"darwin"
|
|
@@ -470,8 +470,8 @@
|
|
|
470
470
|
"optional": true
|
|
471
471
|
},
|
|
472
472
|
"node_modules/@bastani/atomic-natives-linux-arm64-gnu": {
|
|
473
|
-
"version": "0.9.40
|
|
474
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-gnu/-/atomic-natives-linux-arm64-gnu-0.9.40
|
|
473
|
+
"version": "0.9.40",
|
|
474
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-gnu/-/atomic-natives-linux-arm64-gnu-0.9.40.tgz",
|
|
475
475
|
"license": "MIT",
|
|
476
476
|
"os": [
|
|
477
477
|
"linux"
|
|
@@ -485,8 +485,8 @@
|
|
|
485
485
|
"optional": true
|
|
486
486
|
},
|
|
487
487
|
"node_modules/@bastani/atomic-natives-linux-arm64-musl": {
|
|
488
|
-
"version": "0.9.40
|
|
489
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-musl/-/atomic-natives-linux-arm64-musl-0.9.40
|
|
488
|
+
"version": "0.9.40",
|
|
489
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-musl/-/atomic-natives-linux-arm64-musl-0.9.40.tgz",
|
|
490
490
|
"license": "MIT",
|
|
491
491
|
"os": [
|
|
492
492
|
"linux"
|
|
@@ -500,8 +500,8 @@
|
|
|
500
500
|
"optional": true
|
|
501
501
|
},
|
|
502
502
|
"node_modules/@bastani/atomic-natives-linux-x64-gnu": {
|
|
503
|
-
"version": "0.9.40
|
|
504
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-gnu/-/atomic-natives-linux-x64-gnu-0.9.40
|
|
503
|
+
"version": "0.9.40",
|
|
504
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-gnu/-/atomic-natives-linux-x64-gnu-0.9.40.tgz",
|
|
505
505
|
"license": "MIT",
|
|
506
506
|
"os": [
|
|
507
507
|
"linux"
|
|
@@ -515,8 +515,8 @@
|
|
|
515
515
|
"optional": true
|
|
516
516
|
},
|
|
517
517
|
"node_modules/@bastani/atomic-natives-linux-x64-musl": {
|
|
518
|
-
"version": "0.9.40
|
|
519
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-musl/-/atomic-natives-linux-x64-musl-0.9.40
|
|
518
|
+
"version": "0.9.40",
|
|
519
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-musl/-/atomic-natives-linux-x64-musl-0.9.40.tgz",
|
|
520
520
|
"license": "MIT",
|
|
521
521
|
"os": [
|
|
522
522
|
"linux"
|
|
@@ -530,8 +530,8 @@
|
|
|
530
530
|
"optional": true
|
|
531
531
|
},
|
|
532
532
|
"node_modules/@bastani/atomic-natives-win32-arm64-msvc": {
|
|
533
|
-
"version": "0.9.40
|
|
534
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-arm64-msvc/-/atomic-natives-win32-arm64-msvc-0.9.40
|
|
533
|
+
"version": "0.9.40",
|
|
534
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-arm64-msvc/-/atomic-natives-win32-arm64-msvc-0.9.40.tgz",
|
|
535
535
|
"license": "MIT",
|
|
536
536
|
"os": [
|
|
537
537
|
"win32"
|
|
@@ -542,8 +542,8 @@
|
|
|
542
542
|
"optional": true
|
|
543
543
|
},
|
|
544
544
|
"node_modules/@bastani/atomic-natives-win32-x64-msvc": {
|
|
545
|
-
"version": "0.9.40
|
|
546
|
-
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-x64-msvc/-/atomic-natives-win32-x64-msvc-0.9.40
|
|
545
|
+
"version": "0.9.40",
|
|
546
|
+
"resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-x64-msvc/-/atomic-natives-win32-x64-msvc-0.9.40.tgz",
|
|
547
547
|
"license": "MIT",
|
|
548
548
|
"os": [
|
|
549
549
|
"win32"
|
|
@@ -554,8 +554,8 @@
|
|
|
554
554
|
"optional": true
|
|
555
555
|
},
|
|
556
556
|
"node_modules/@bastani/pi-ai": {
|
|
557
|
-
"version": "0.9.40
|
|
558
|
-
"resolved": "https://registry.npmjs.org/@bastani/pi-ai/-/pi-ai-0.9.40
|
|
557
|
+
"version": "0.9.40",
|
|
558
|
+
"resolved": "https://registry.npmjs.org/@bastani/pi-ai/-/pi-ai-0.9.40.tgz",
|
|
559
559
|
"license": "MIT",
|
|
560
560
|
"dependencies": {
|
|
561
561
|
"@anthropic-ai/sdk": "0.129.0",
|
|
@@ -2262,9 +2262,9 @@
|
|
|
2262
2262
|
}
|
|
2263
2263
|
},
|
|
2264
2264
|
"node_modules/hono": {
|
|
2265
|
-
"version": "4.13.
|
|
2266
|
-
"resolved": "https://registry.npmjs.org/hono/-/hono-4.13.
|
|
2267
|
-
"integrity": "sha512-
|
|
2265
|
+
"version": "4.13.13",
|
|
2266
|
+
"resolved": "https://registry.npmjs.org/hono/-/hono-4.13.13.tgz",
|
|
2267
|
+
"integrity": "sha512-CQ46U0ZkAGmbT/4UxdzzGJpacP2IeKgY4a5/tOI9AABbpOMfK739wfDXmv1usCk+3RkKj1hQy4/fjhiwa2xlrA==",
|
|
2268
2268
|
"license": "MIT",
|
|
2269
2269
|
"engines": {
|
|
2270
2270
|
"node": ">=16.9.0"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/atomic",
|
|
3
|
-
"version": "0.9.40
|
|
3
|
+
"version": "0.9.40",
|
|
4
4
|
"description": "Atomic coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"atomicConfig": {
|
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
"start:fast": "bun run bundle:dev && bun dist-dev/cli.js",
|
|
77
77
|
"build:binary": "bun run clean && bun run build && bun build --target=bun --format=cjs --minify-syntax --external mupdf --external=*native-modifiers.js ./dist/bun/cli.js --outfile dist/app.js && bun build --target=bun --format=cjs --external mupdf ./src/utils/image-resize-worker.ts --outfile dist/image-resize-worker.js && bun run build:codemode-worker && bun build --compile --bytecode --format=cjs --external mupdf --no-compile-autoload-dotenv --no-compile-autoload-bunfig ./dist/bun/split-loader.js --outfile dist/atomic && bun run copy-binary-assets && bun run assert-binary-assets",
|
|
78
78
|
"build:codemode-worker": "bun build --target=bun --format=esm ./src/extensions/codemode/worker.ts --outfile dist/codemode-worker.js && shx cp ../../node_modules/quickjs-wasi/quickjs.wasm dist/",
|
|
79
|
-
"copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/modes/interactive/assets && shx cp src/modes/interactive/assets/*.png dist/modes/interactive/assets/ && shx mkdir -p dist/core/export-html/vendor
|
|
80
|
-
"copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/assets && shx cp src/modes/interactive/assets/*.png dist/assets/ && shx mkdir -p dist/export-html/vendor
|
|
79
|
+
"copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/modes/interactive/assets && shx cp src/modes/interactive/assets/*.png dist/modes/interactive/assets/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/ && bun run copy-builtin-packages",
|
|
80
|
+
"copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/assets && shx cp src/modes/interactive/assets/*.png dist/assets/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp ../../node_modules/@earendil-works/pi-tui/dist/native-modifiers.js dist/ && shx cp ../../node_modules/@earendil-works/pi-tui/dist/native-module-path.js dist/ && shx cp ../../node_modules/@earendil-works/pi-tui/dist/native-platform.js dist/ && shx mkdir -p dist/native/win32/prebuilds && shx cp -r ../../node_modules/@earendil-works/pi-tui/native/win32/prebuilds/win32-* dist/native/win32/prebuilds/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/ && bun run copy-builtin-packages",
|
|
81
81
|
"copy-builtin-packages": "bun run scripts/copy-builtin-packages.ts",
|
|
82
82
|
"copy-builtin-workflows": "bun run copy-builtin-packages",
|
|
83
83
|
"assert-binary-assets": "bun run scripts/assert-pi-runtime-assets.ts --app dist/app.js",
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
"prepublishOnly": "bun run clean && bun run build && bun run shrinkwrap"
|
|
90
90
|
},
|
|
91
91
|
"dependencies": {
|
|
92
|
-
"@bastani/atomic-natives": "0.9.40
|
|
93
|
-
"@bastani/pi-ai": "0.9.40
|
|
92
|
+
"@bastani/atomic-natives": "0.9.40",
|
|
93
|
+
"@bastani/pi-ai": "0.9.40",
|
|
94
94
|
"@earendil-works/pi-agent-core": "1.3.0",
|
|
95
95
|
"@earendil-works/pi-codemode": "1.3.0",
|
|
96
96
|
"@earendil-works/pi-tui": "1.3.0",
|
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
(function() {
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
// ============================================================
|
|
5
|
-
// DATA LOADING
|
|
6
|
-
// ============================================================
|
|
7
|
-
|
|
8
|
-
const base64 = document.getElementById('session-data').textContent;
|
|
9
|
-
const binary = atob(base64);
|
|
10
|
-
const bytes = new Uint8Array(binary.length);
|
|
11
|
-
for (let i = 0; i < binary.length; i++) {
|
|
12
|
-
bytes[i] = binary.charCodeAt(i);
|
|
13
|
-
}
|
|
14
|
-
const data = JSON.parse(new TextDecoder('utf-8').decode(bytes));
|
|
15
|
-
const { header, entries, leafId: defaultLeafId, systemPrompt, tools, renderedTools } = data;
|
|
16
|
-
|
|
17
|
-
// ============================================================
|
|
18
|
-
// URL PARAMETER HANDLING
|
|
19
|
-
// ============================================================
|
|
20
|
-
|
|
21
|
-
// Parse URL parameters for deep linking: leafId and targetId
|
|
22
|
-
// Check for injected params (when loaded in iframe via srcdoc) or use window.location
|
|
23
|
-
const injectedParams = document.querySelector('meta[name="pi-url-params"]');
|
|
24
|
-
const searchString = injectedParams ? injectedParams.content : window.location.search.substring(1);
|
|
25
|
-
const urlParams = new URLSearchParams(searchString);
|
|
26
|
-
const urlLeafId = urlParams.get('leafId');
|
|
27
|
-
const urlTargetId = urlParams.get('targetId');
|
|
28
|
-
// Use URL leafId if provided, otherwise fall back to session default
|
|
29
|
-
const leafId = urlLeafId || defaultLeafId;
|
|
30
|
-
|
|
31
|
-
// ============================================================
|
|
32
|
-
// DATA STRUCTURES
|
|
33
|
-
// ============================================================
|
|
34
|
-
|
|
35
|
-
// Entry lookup by ID
|
|
36
|
-
const byId = new Map();
|
|
37
|
-
for (const entry of entries) {
|
|
38
|
-
byId.set(entry.id, entry);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Tool call lookup (toolCallId -> {name, arguments})
|
|
42
|
-
const toolCallMap = new Map();
|
|
43
|
-
for (const entry of entries) {
|
|
44
|
-
if (entry.type === 'message' && entry.message.role === 'assistant') {
|
|
45
|
-
const content = entry.message.content;
|
|
46
|
-
if (Array.isArray(content)) {
|
|
47
|
-
for (const block of content) {
|
|
48
|
-
if (block.type === 'toolCall') {
|
|
49
|
-
toolCallMap.set(block.id, { name: block.name, arguments: block.arguments });
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Label lookup (entryId -> label string)
|
|
57
|
-
// Labels are stored in 'label' entries that reference their target via targetId
|
|
58
|
-
const labelMap = new Map();
|
|
59
|
-
for (const entry of entries) {
|
|
60
|
-
if (entry.type === 'label' && entry.targetId && entry.label) {
|
|
61
|
-
labelMap.set(entry.targetId, entry.label);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ============================================================
|
|
66
|
-
// TREE DATA PREPARATION (no DOM, pure data)
|
|
67
|
-
// ============================================================
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Build tree structure from flat entries.
|
|
71
|
-
* Returns array of root nodes, each with { entry, children, label }.
|
|
72
|
-
*/
|
|
73
|
-
function buildTree() {
|
|
74
|
-
const nodeMap = new Map();
|
|
75
|
-
const roots = [];
|
|
76
|
-
|
|
77
|
-
// Create nodes
|
|
78
|
-
for (const entry of entries) {
|
|
79
|
-
nodeMap.set(entry.id, {
|
|
80
|
-
entry,
|
|
81
|
-
children: [],
|
|
82
|
-
label: labelMap.get(entry.id)
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Build parent-child relationships
|
|
87
|
-
for (const entry of entries) {
|
|
88
|
-
const node = nodeMap.get(entry.id);
|
|
89
|
-
if (entry.parentId === null || entry.parentId === undefined || entry.parentId === entry.id) {
|
|
90
|
-
roots.push(node);
|
|
91
|
-
} else {
|
|
92
|
-
const parent = nodeMap.get(entry.parentId);
|
|
93
|
-
if (parent) {
|
|
94
|
-
parent.children.push(node);
|
|
95
|
-
} else {
|
|
96
|
-
roots.push(node);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Sort children by timestamp
|
|
102
|
-
function sortChildren(node) {
|
|
103
|
-
node.children.sort((a, b) =>
|
|
104
|
-
new Date(a.entry.timestamp).getTime() - new Date(b.entry.timestamp).getTime()
|
|
105
|
-
);
|
|
106
|
-
node.children.forEach(sortChildren);
|
|
107
|
-
}
|
|
108
|
-
roots.forEach(sortChildren);
|
|
109
|
-
|
|
110
|
-
return roots;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Build set of entry IDs on path from root to target.
|
|
115
|
-
*/
|
|
116
|
-
function buildActivePathIds(targetId) {
|
|
117
|
-
const ids = new Set();
|
|
118
|
-
let current = byId.get(targetId);
|
|
119
|
-
while (current) {
|
|
120
|
-
ids.add(current.id);
|
|
121
|
-
// Stop if no parent or self-referencing (root)
|
|
122
|
-
if (!current.parentId || current.parentId === current.id) {
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
125
|
-
current = byId.get(current.parentId);
|
|
126
|
-
}
|
|
127
|
-
return ids;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Get array of entries from root to target (the conversation path).
|
|
132
|
-
*/
|
|
133
|
-
function getPath(targetId) {
|
|
134
|
-
const path = [];
|
|
135
|
-
let current = byId.get(targetId);
|
|
136
|
-
while (current) {
|
|
137
|
-
path.unshift(current);
|
|
138
|
-
// Stop if no parent or self-referencing (root)
|
|
139
|
-
if (!current.parentId || current.parentId === current.id) {
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
current = byId.get(current.parentId);
|
|
143
|
-
}
|
|
144
|
-
return path;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Tree node lookup for finding leaves
|
|
148
|
-
let treeNodeMap = null;
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Find the newest leaf node reachable from a given node.
|
|
152
|
-
* This allows clicking any node in a branch to show the full branch.
|
|
153
|
-
* Children are sorted by timestamp, so the newest is always last.
|
|
154
|
-
*/
|
|
155
|
-
function findNewestLeaf(nodeId) {
|
|
156
|
-
// Build tree node map lazily
|
|
157
|
-
if (!treeNodeMap) {
|
|
158
|
-
treeNodeMap = new Map();
|
|
159
|
-
const tree = buildTree();
|
|
160
|
-
function mapNodes(node) {
|
|
161
|
-
treeNodeMap.set(node.entry.id, node);
|
|
162
|
-
node.children.forEach(mapNodes);
|
|
163
|
-
}
|
|
164
|
-
tree.forEach(mapNodes);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const node = treeNodeMap.get(nodeId);
|
|
168
|
-
if (!node) return nodeId;
|
|
169
|
-
|
|
170
|
-
// Follow the newest (last) child at each level
|
|
171
|
-
let current = node;
|
|
172
|
-
while (current.children.length > 0) {
|
|
173
|
-
current = current.children[current.children.length - 1];
|
|
174
|
-
}
|
|
175
|
-
return current.entry.id;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Flatten tree into list with indentation and connector info.
|
|
180
|
-
* Returns array of { node, indent, showConnector, isLast, gutters, isVirtualRootChild, multipleRoots }.
|
|
181
|
-
* Matches tree-selector.ts logic exactly.
|
|
182
|
-
*/
|
|
183
|
-
function flattenTree(roots, activePathIds) {
|
|
184
|
-
const result = [];
|
|
185
|
-
const multipleRoots = roots.length > 1;
|
|
186
|
-
|
|
187
|
-
// Mark which subtrees contain the active leaf
|
|
188
|
-
const containsActive = new Map();
|
|
189
|
-
function markActive(node) {
|
|
190
|
-
let has = activePathIds.has(node.entry.id);
|
|
191
|
-
for (const child of node.children) {
|
|
192
|
-
if (markActive(child)) has = true;
|
|
193
|
-
}
|
|
194
|
-
containsActive.set(node, has);
|
|
195
|
-
return has;
|
|
196
|
-
}
|
|
197
|
-
roots.forEach(markActive);
|
|
198
|
-
|
|
199
|
-
// Stack: [node, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild]
|
|
200
|
-
const stack = [];
|
|
201
|
-
|
|
202
|
-
// Add roots (prioritize branch containing active leaf)
|
|
203
|
-
const orderedRoots = [...roots].sort((a, b) =>
|
|
204
|
-
Number(containsActive.get(b)) - Number(containsActive.get(a))
|
|
205
|
-
);
|
|
206
|
-
for (let i = orderedRoots.length - 1; i >= 0; i--) {
|
|
207
|
-
const isLast = i === orderedRoots.length - 1;
|
|
208
|
-
stack.push([orderedRoots[i], multipleRoots ? 1 : 0, multipleRoots, multipleRoots, isLast, [], multipleRoots]);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
while (stack.length > 0) {
|
|
212
|
-
const [node, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild] = stack.pop();
|
|
213
|
-
|
|
214
|
-
result.push({ node, indent, showConnector, isLast, gutters, isVirtualRootChild, multipleRoots });
|
|
215
|
-
|
|
216
|
-
const children = node.children;
|
|
217
|
-
const multipleChildren = children.length > 1;
|
|
218
|
-
|
|
219
|
-
// Order children (active branch first)
|
|
220
|
-
const orderedChildren = [...children].sort((a, b) =>
|
|
221
|
-
Number(containsActive.get(b)) - Number(containsActive.get(a))
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
// Calculate child indent (matches tree-selector.ts)
|
|
225
|
-
let childIndent;
|
|
226
|
-
if (multipleChildren) {
|
|
227
|
-
// Parent branches: children get +1
|
|
228
|
-
childIndent = indent + 1;
|
|
229
|
-
} else if (justBranched && indent > 0) {
|
|
230
|
-
// First generation after a branch: +1 for visual grouping
|
|
231
|
-
childIndent = indent + 1;
|
|
232
|
-
} else {
|
|
233
|
-
// Single-child chain: stay flat
|
|
234
|
-
childIndent = indent;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Build gutters for children
|
|
238
|
-
const connectorDisplayed = showConnector && !isVirtualRootChild;
|
|
239
|
-
const currentDisplayIndent = multipleRoots ? Math.max(0, indent - 1) : indent;
|
|
240
|
-
const connectorPosition = Math.max(0, currentDisplayIndent - 1);
|
|
241
|
-
const childGutters = connectorDisplayed
|
|
242
|
-
? [...gutters, { position: connectorPosition, show: !isLast }]
|
|
243
|
-
: gutters;
|
|
244
|
-
|
|
245
|
-
// Add children in reverse order for stack
|
|
246
|
-
for (let i = orderedChildren.length - 1; i >= 0; i--) {
|
|
247
|
-
const childIsLast = i === orderedChildren.length - 1;
|
|
248
|
-
stack.push([orderedChildren[i], childIndent, multipleChildren, multipleChildren, childIsLast, childGutters, false]);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return result;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Build ASCII prefix string for tree node.
|
|
257
|
-
*/
|
|
258
|
-
function buildTreePrefix(flatNode) {
|
|
259
|
-
const { indent, showConnector, isLast, gutters, isVirtualRootChild, multipleRoots } = flatNode;
|
|
260
|
-
const displayIndent = multipleRoots ? Math.max(0, indent - 1) : indent;
|
|
261
|
-
const connector = showConnector && !isVirtualRootChild ? (isLast ? '└─ ' : '├─ ') : '';
|
|
262
|
-
const connectorPosition = connector ? displayIndent - 1 : -1;
|
|
263
|
-
|
|
264
|
-
const totalChars = displayIndent * 3;
|
|
265
|
-
const prefixChars = [];
|
|
266
|
-
for (let i = 0; i < totalChars; i++) {
|
|
267
|
-
const level = Math.floor(i / 3);
|
|
268
|
-
const posInLevel = i % 3;
|
|
269
|
-
|
|
270
|
-
const gutter = gutters.find(g => g.position === level);
|
|
271
|
-
if (gutter) {
|
|
272
|
-
prefixChars.push(posInLevel === 0 ? (gutter.show ? '│' : ' ') : ' ');
|
|
273
|
-
} else if (connector && level === connectorPosition) {
|
|
274
|
-
if (posInLevel === 0) {
|
|
275
|
-
prefixChars.push(isLast ? '└' : '├');
|
|
276
|
-
} else if (posInLevel === 1) {
|
|
277
|
-
prefixChars.push('─');
|
|
278
|
-
} else {
|
|
279
|
-
prefixChars.push(' ');
|
|
280
|
-
}
|
|
281
|
-
} else {
|
|
282
|
-
prefixChars.push(' ');
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
return prefixChars.join('');
|
|
286
|
-
}
|
|
287
|
-
|