@nexus-aethra/dshell-conversation 0.1.5 → 0.1.7
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/lib/client/index.js +76 -49
- package/lib/client/index.js.map +1 -1
- package/lib/client.js +60 -28
- package/lib/client.js.map +1 -1
- package/lib/types/client/index.d.ts +16 -17
- package/lib/types/client/index.d.ts.map +1 -1
- package/package.json +8 -8
package/lib/client/index.js
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* dshell-conversation browser face —
|
|
2
|
+
* dshell-conversation browser face — the `terminal` conversation target.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* surface — no separate view tab — keeping PTY output and the input
|
|
11
|
-
* line fused in one column.
|
|
4
|
+
* The target is what takes a session out of dsh's centered hero phase and into
|
|
5
|
+
* the docked composer: ui-conversation resolves the shell phase from active
|
|
6
|
+
* targets, and its restore path falls back to `chat` whenever no preference
|
|
7
|
+
* exists. Registering it unconditionally (the old shape) forced EVERY session —
|
|
8
|
+
* including stock ones — into the docked layout, which is exactly the native
|
|
9
|
+
* surface this package must now leave alone.
|
|
12
10
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* precedence
|
|
11
|
+
* So the target, and the auto-activation that steers an opted-in session onto
|
|
12
|
+
* it, exist only while the CURRENT session carries the terminal-mode flag. The
|
|
13
|
+
* flag lives in dshell-mode's browser half and is reached by service key: a
|
|
14
|
+
* composition without it simply never registers the target.
|
|
15
|
+
*
|
|
16
|
+
* A subsequent `selectView` from the reader (the strip's 会话 tab) takes
|
|
17
|
+
* precedence through the stock path — activation only steers the default, and
|
|
18
|
+
* the strip's selection is a separate per-session preference.
|
|
20
19
|
*/
|
|
21
20
|
import { mainSessionId } from '@nexus-aethra/dshell-std';
|
|
22
21
|
export const name = '@nexus-aethra/dshell-conversation/client';
|
|
@@ -38,49 +37,77 @@ const viewDefinition = {
|
|
|
38
37
|
// its docked composer instead of the centered hero.
|
|
39
38
|
isActive: () => true,
|
|
40
39
|
};
|
|
41
|
-
/** Register the
|
|
40
|
+
/** Register the `terminal` target and its activation only for opted-in sessions. */
|
|
42
41
|
export function apply(ctx) {
|
|
43
42
|
// Cast through unknown: the 'sessions' key collides across faces in one
|
|
44
43
|
// tsc program (host SessionStore vs client ISessions); see terminal-bridge.
|
|
45
44
|
const sessions = ctx.get('sessions');
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// This asserts the terminal *target* (which snapshots the conversation
|
|
57
|
-
// assembles), not the user's tab choice — the strip's selection is a
|
|
58
|
-
// separate per-session preference the store owns, and `activate` never
|
|
59
|
-
// touches it. So the strip can offer `会话` / `轨迹` and a click on
|
|
60
|
-
// `轨迹` stays put: it writes the preference through the stock
|
|
61
|
-
// selectView path, and the reconcile that follows a session switch only
|
|
62
|
-
// re-adds `terminal` to a monotonic active set.
|
|
45
|
+
let modes;
|
|
46
|
+
let resync;
|
|
47
|
+
// Reached by service key with a structural cast, exactly as this bundle reads
|
|
48
|
+
// the SSH and buffer seats: a composition without dshell-mode simply never
|
|
49
|
+
// registers the target instead of failing to load.
|
|
50
|
+
const modeHost = ctx;
|
|
51
|
+
modeHost.inject(['dshellTerminalMode'], (scope) => {
|
|
52
|
+
modes = scope.dshellTerminalMode;
|
|
53
|
+
resync?.();
|
|
54
|
+
});
|
|
63
55
|
ctx.effect(() => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
56
|
+
let stop;
|
|
57
|
+
let held;
|
|
58
|
+
const sync = () => {
|
|
67
59
|
const current = mainSessionId(Object.values(sessions.list.getSnapshot().byId));
|
|
68
|
-
|
|
60
|
+
const wanted = current !== undefined && modes?.isOn(String(current)) === true
|
|
61
|
+
? String(current)
|
|
62
|
+
: undefined;
|
|
63
|
+
if (wanted === held)
|
|
64
|
+
return;
|
|
65
|
+
stop?.();
|
|
66
|
+
stop = undefined;
|
|
67
|
+
held = wanted;
|
|
68
|
+
if (wanted === undefined)
|
|
69
69
|
return;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
const disposeRegister = ctx.uiConversation.views.register(viewDefinition);
|
|
71
|
+
// The terminal target must be the active view or the shell renders the
|
|
72
|
+
// (empty) chat transcript instead. Re-assert on every session-list and
|
|
73
|
+
// view-slot change — these subscriptions are registered after
|
|
74
|
+
// ui-conversation's, so this activation runs last and wins the tick.
|
|
75
|
+
// `activate` is idempotent (the assembler ignores a target already active).
|
|
76
|
+
const disposeReconcile = ctx.effect(() => {
|
|
77
|
+
const reconcile = () => {
|
|
78
|
+
const now = mainSessionId(Object.values(sessions.list.getSnapshot().byId));
|
|
79
|
+
if (now === undefined || String(now) !== held)
|
|
80
|
+
return;
|
|
81
|
+
try {
|
|
82
|
+
ctx.uiConversation.binding(now).activate('terminal');
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Session scope not materialized yet; retry on the next change.
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const disposeList = sessions.list.subscribe(reconcile);
|
|
89
|
+
const disposeViews = ctx.slots.subscribe('conversation.view', reconcile);
|
|
90
|
+
reconcile();
|
|
91
|
+
return () => {
|
|
92
|
+
disposeList();
|
|
93
|
+
disposeViews();
|
|
94
|
+
};
|
|
95
|
+
}, 'dshell-conversation: open opted-in session into terminal view');
|
|
96
|
+
stop = () => {
|
|
97
|
+
disposeReconcile();
|
|
98
|
+
disposeRegister();
|
|
99
|
+
};
|
|
76
100
|
};
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
101
|
+
resync = sync;
|
|
102
|
+
const disposeList = sessions.list.subscribe(sync);
|
|
103
|
+
const disposeModes = modes?.subscribe(sync);
|
|
104
|
+
sync();
|
|
80
105
|
return () => {
|
|
106
|
+
resync = undefined;
|
|
81
107
|
disposeList();
|
|
82
|
-
|
|
108
|
+
disposeModes?.();
|
|
109
|
+
stop?.();
|
|
83
110
|
};
|
|
84
|
-
}, 'dshell-conversation:
|
|
111
|
+
}, 'dshell-conversation: terminal target for opted-in sessions');
|
|
85
112
|
}
|
|
86
113
|
//# sourceMappingURL=index.js.map
|
package/lib/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|
package/lib/client.js
CHANGED
|
@@ -35,24 +35,23 @@ window.__ModuleLoader__.load({
|
|
|
35
35
|
//#endregion
|
|
36
36
|
//#region lib/client/index.js
|
|
37
37
|
/**
|
|
38
|
-
* dshell-conversation browser face —
|
|
38
|
+
* dshell-conversation browser face — the `terminal` conversation target.
|
|
39
39
|
*
|
|
40
|
-
* The
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* surface — no separate view tab — keeping PTY output and the input
|
|
47
|
-
* line fused in one column.
|
|
40
|
+
* The target is what takes a session out of dsh's centered hero phase and into
|
|
41
|
+
* the docked composer: ui-conversation resolves the shell phase from active
|
|
42
|
+
* targets, and its restore path falls back to `chat` whenever no preference
|
|
43
|
+
* exists. Registering it unconditionally (the old shape) forced EVERY session —
|
|
44
|
+
* including stock ones — into the docked layout, which is exactly the native
|
|
45
|
+
* surface this package must now leave alone.
|
|
48
46
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* precedence
|
|
47
|
+
* So the target, and the auto-activation that steers an opted-in session onto
|
|
48
|
+
* it, exist only while the CURRENT session carries the terminal-mode flag. The
|
|
49
|
+
* flag lives in dshell-mode's browser half and is reached by service key: a
|
|
50
|
+
* composition without it simply never registers the target.
|
|
51
|
+
*
|
|
52
|
+
* A subsequent `selectView` from the reader (the strip's 会话 tab) takes
|
|
53
|
+
* precedence through the stock path — activation only steers the default, and
|
|
54
|
+
* the strip's selection is a separate per-session preference.
|
|
56
55
|
*/
|
|
57
56
|
const name = "@nexus-aethra/dshell-conversation/client";
|
|
58
57
|
const inject = [
|
|
@@ -74,26 +73,59 @@ window.__ModuleLoader__.load({
|
|
|
74
73
|
create: () => new TerminalViewBuilder(),
|
|
75
74
|
isActive: () => true
|
|
76
75
|
};
|
|
77
|
-
/** Register the
|
|
76
|
+
/** Register the `terminal` target and its activation only for opted-in sessions. */
|
|
78
77
|
function apply(ctx) {
|
|
79
78
|
const sessions = ctx.get("sessions");
|
|
80
|
-
|
|
79
|
+
let modes;
|
|
80
|
+
let resync;
|
|
81
|
+
ctx.inject(["dshellTerminalMode"], (scope) => {
|
|
82
|
+
modes = scope.dshellTerminalMode;
|
|
83
|
+
resync?.();
|
|
84
|
+
});
|
|
81
85
|
ctx.effect(() => {
|
|
82
|
-
|
|
86
|
+
let stop;
|
|
87
|
+
let held;
|
|
88
|
+
const sync = () => {
|
|
83
89
|
const current = mainSessionId(Object.values(sessions.list.getSnapshot().byId));
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
const wanted = current !== void 0 && modes?.isOn(String(current)) === true ? String(current) : void 0;
|
|
91
|
+
if (wanted === held) return;
|
|
92
|
+
stop?.();
|
|
93
|
+
stop = void 0;
|
|
94
|
+
held = wanted;
|
|
95
|
+
if (wanted === void 0) return;
|
|
96
|
+
const disposeRegister = ctx.uiConversation.views.register(viewDefinition);
|
|
97
|
+
const disposeReconcile = ctx.effect(() => {
|
|
98
|
+
const reconcile = () => {
|
|
99
|
+
const now = mainSessionId(Object.values(sessions.list.getSnapshot().byId));
|
|
100
|
+
if (now === void 0 || String(now) !== held) return;
|
|
101
|
+
try {
|
|
102
|
+
ctx.uiConversation.binding(now).activate("terminal");
|
|
103
|
+
} catch {}
|
|
104
|
+
};
|
|
105
|
+
const disposeList = sessions.list.subscribe(reconcile);
|
|
106
|
+
const disposeViews = ctx.slots.subscribe("conversation.view", reconcile);
|
|
107
|
+
reconcile();
|
|
108
|
+
return () => {
|
|
109
|
+
disposeList();
|
|
110
|
+
disposeViews();
|
|
111
|
+
};
|
|
112
|
+
}, "dshell-conversation: open opted-in session into terminal view");
|
|
113
|
+
stop = () => {
|
|
114
|
+
disposeReconcile();
|
|
115
|
+
disposeRegister();
|
|
116
|
+
};
|
|
88
117
|
};
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
118
|
+
resync = sync;
|
|
119
|
+
const disposeList = sessions.list.subscribe(sync);
|
|
120
|
+
const disposeModes = modes?.subscribe(sync);
|
|
121
|
+
sync();
|
|
92
122
|
return () => {
|
|
123
|
+
resync = void 0;
|
|
93
124
|
disposeList();
|
|
94
|
-
|
|
125
|
+
disposeModes?.();
|
|
126
|
+
stop?.();
|
|
95
127
|
};
|
|
96
|
-
}, "dshell-conversation:
|
|
128
|
+
}, "dshell-conversation: terminal target for opted-in sessions");
|
|
97
129
|
}
|
|
98
130
|
//#endregion
|
|
99
131
|
exports.apply = apply;
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":[],"sources":["../../std/lib/session-view.js","client/index.js"],"sourcesContent":["/**\n * Which Session the main view is holding.\n *\n * The host's rule, taken once. `0.1.5-rc.2` published the Session on screen as\n * `sessions.list.current`; `0.1.6-alpha.2` removed that field, because a Client\n * may now hold several generations at once and no service owns \"the one on\n * screen\" — navigation belongs to view owners. What replaced the field is\n * RETENTION: the surface displaying a Session claims it with\n * `retain(target, { source: 'mainView' })`, and the Session on screen is the row\n * whose `mainView` count is positive.\n *\n * The derivation is what the host itself repeats wherever it needs the answer —\n * `ui-workspace/tree.ts`, `ui-layout/DocumentTitle.tsx`, `ui-session`,\n * `ui-settings-general` all write it out — so reading it here is not a dshell\n * invention, it is the same reading taken once instead of once per package.\n *\n * `undefined` means no view holds a Session: on a page that has not navigated\n * yet, and in the window between a release and the next retain.\n */\n/**\n * The Session the main view is holding, by the host's own rule.\n * @param rows - the session-list rows, in any order.\n * @returns the held Session's id, or undefined when no view holds one.\n */\nexport function mainSessionId(rows) {\n for (const row of rows) {\n // The cast is the point of the structural parameter: the host publishes a\n // frozen partial record whose source names are its own, and the one this\n // rule reads is `mainView`.\n const counts = row.retainedBy;\n if ((counts?.mainView ?? 0) > 0)\n return row.id;\n }\n return undefined;\n}\n//# sourceMappingURL=session-view.js.map","/**\n * dshell-conversation browser face —
|
|
1
|
+
{"version":3,"file":"client.js","names":[],"sources":["../../std/lib/session-view.js","client/index.js"],"sourcesContent":["/**\n * Which Session the main view is holding.\n *\n * The host's rule, taken once. `0.1.5-rc.2` published the Session on screen as\n * `sessions.list.current`; `0.1.6-alpha.2` removed that field, because a Client\n * may now hold several generations at once and no service owns \"the one on\n * screen\" — navigation belongs to view owners. What replaced the field is\n * RETENTION: the surface displaying a Session claims it with\n * `retain(target, { source: 'mainView' })`, and the Session on screen is the row\n * whose `mainView` count is positive.\n *\n * The derivation is what the host itself repeats wherever it needs the answer —\n * `ui-workspace/tree.ts`, `ui-layout/DocumentTitle.tsx`, `ui-session`,\n * `ui-settings-general` all write it out — so reading it here is not a dshell\n * invention, it is the same reading taken once instead of once per package.\n *\n * `undefined` means no view holds a Session: on a page that has not navigated\n * yet, and in the window between a release and the next retain.\n */\n/**\n * The Session the main view is holding, by the host's own rule.\n * @param rows - the session-list rows, in any order.\n * @returns the held Session's id, or undefined when no view holds one.\n */\nexport function mainSessionId(rows) {\n for (const row of rows) {\n // The cast is the point of the structural parameter: the host publishes a\n // frozen partial record whose source names are its own, and the one this\n // rule reads is `mainView`.\n const counts = row.retainedBy;\n if ((counts?.mainView ?? 0) > 0)\n return row.id;\n }\n return undefined;\n}\n//# sourceMappingURL=session-view.js.map","/**\n * dshell-conversation browser face — the `terminal` conversation target.\n *\n * The target is what takes a session out of dsh's centered hero phase and into\n * the docked composer: ui-conversation resolves the shell phase from active\n * targets, and its restore path falls back to `chat` whenever no preference\n * exists. Registering it unconditionally (the old shape) forced EVERY session —\n * including stock ones — into the docked layout, which is exactly the native\n * surface this package must now leave alone.\n *\n * So the target, and the auto-activation that steers an opted-in session onto\n * it, exist only while the CURRENT session carries the terminal-mode flag. The\n * flag lives in dshell-mode's browser half and is reached by service key: a\n * composition without it simply never registers the target.\n *\n * A subsequent `selectView` from the reader (the strip's 会话 tab) takes\n * precedence through the stock path — activation only steers the default, and\n * the strip's selection is a separate per-session preference.\n */\nimport { mainSessionId } from '@nexus-aethra/dshell-std';\nexport const name = '@nexus-aethra/dshell-conversation/client';\nexport const inject = ['uiConversation', 'sessions', 'slots'];\nclass TerminalViewBuilder {\n empty = { rows: [] };\n replace() {\n return this.empty;\n }\n apply() {\n return this.empty;\n }\n}\nconst viewDefinition = {\n target: 'terminal',\n create: () => new TerminalViewBuilder(),\n // The terminal surface IS dshell's content (design 4.1): it counts as\n // visible activity even in a blank session, so the conversation renders\n // its docked composer instead of the centered hero.\n isActive: () => true,\n};\n/** Register the `terminal` target and its activation only for opted-in sessions. */\nexport function apply(ctx) {\n // Cast through unknown: the 'sessions' key collides across faces in one\n // tsc program (host SessionStore vs client ISessions); see terminal-bridge.\n const sessions = ctx.get('sessions');\n let modes;\n let resync;\n // Reached by service key with a structural cast, exactly as this bundle reads\n // the SSH and buffer seats: a composition without dshell-mode simply never\n // registers the target instead of failing to load.\n const modeHost = ctx;\n modeHost.inject(['dshellTerminalMode'], (scope) => {\n modes = scope.dshellTerminalMode;\n resync?.();\n });\n ctx.effect(() => {\n let stop;\n let held;\n const sync = () => {\n const current = mainSessionId(Object.values(sessions.list.getSnapshot().byId));\n const wanted = current !== undefined && modes?.isOn(String(current)) === true\n ? String(current)\n : undefined;\n if (wanted === held)\n return;\n stop?.();\n stop = undefined;\n held = wanted;\n if (wanted === undefined)\n return;\n const disposeRegister = ctx.uiConversation.views.register(viewDefinition);\n // The terminal target must be the active view or the shell renders the\n // (empty) chat transcript instead. Re-assert on every session-list and\n // view-slot change — these subscriptions are registered after\n // ui-conversation's, so this activation runs last and wins the tick.\n // `activate` is idempotent (the assembler ignores a target already active).\n const disposeReconcile = ctx.effect(() => {\n const reconcile = () => {\n const now = mainSessionId(Object.values(sessions.list.getSnapshot().byId));\n if (now === undefined || String(now) !== held)\n return;\n try {\n ctx.uiConversation.binding(now).activate('terminal');\n }\n catch {\n // Session scope not materialized yet; retry on the next change.\n }\n };\n const disposeList = sessions.list.subscribe(reconcile);\n const disposeViews = ctx.slots.subscribe('conversation.view', reconcile);\n reconcile();\n return () => {\n disposeList();\n disposeViews();\n };\n }, 'dshell-conversation: open opted-in session into terminal view');\n stop = () => {\n disposeReconcile();\n disposeRegister();\n };\n };\n resync = sync;\n const disposeList = sessions.list.subscribe(sync);\n const disposeModes = modes?.subscribe(sync);\n sync();\n return () => {\n resync = undefined;\n disposeList();\n disposeModes?.();\n stop?.();\n };\n }, 'dshell-conversation: terminal target for opted-in sessions');\n}\n//# sourceMappingURL=index.js.map"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwBA,SAAgB,cAAc,MAAM;GAChC,KAAK,MAAM,OAAO,MAKd,KADe,IAAI,YACN,YAAY,KAAK,GAC1B,OAAO,IAAI;EAGvB;;;;;;;;;;;;;;;;;;;;;;ECdA,MAAa,OAAO;EACpB,MAAa,SAAS;GAAC;GAAkB;GAAY;EAAO;EAC5D,IAAM,sBAAN,MAA0B;GACtB,QAAQ,EAAE,MAAM,CAAC,EAAE;GACnB,UAAU;IACN,OAAO,KAAK;GAChB;GACA,QAAQ;IACJ,OAAO,KAAK;GAChB;EACJ;EACA,MAAM,iBAAiB;GACnB,QAAQ;GACR,cAAc,IAAI,oBAAoB;GAItC,gBAAgB;EACpB;;EAEA,SAAgB,MAAM,KAAK;GAGvB,MAAM,WAAW,IAAI,IAAI,UAAU;GACnC,IAAI;GACJ,IAAI;GAKJ,IAAS,OAAO,CAAC,oBAAoB,IAAI,UAAU;IAC/C,QAAQ,MAAM;IACd,SAAS;GACb,CAAC;GACD,IAAI,aAAa;IACb,IAAI;IACJ,IAAI;IACJ,MAAM,aAAa;KACf,MAAM,UAAU,cAAc,OAAO,OAAO,SAAS,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC;KAC7E,MAAM,SAAS,YAAY,KAAA,KAAa,OAAO,KAAK,OAAO,OAAO,CAAC,MAAM,OACnE,OAAO,OAAO,IACd,KAAA;KACN,IAAI,WAAW,MACX;KACJ,OAAO;KACP,OAAO,KAAA;KACP,OAAO;KACP,IAAI,WAAW,KAAA,GACX;KACJ,MAAM,kBAAkB,IAAI,eAAe,MAAM,SAAS,cAAc;KAMxE,MAAM,mBAAmB,IAAI,aAAa;MACtC,MAAM,kBAAkB;OACpB,MAAM,MAAM,cAAc,OAAO,OAAO,SAAS,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC;OACzE,IAAI,QAAQ,KAAA,KAAa,OAAO,GAAG,MAAM,MACrC;OACJ,IAAI;QACA,IAAI,eAAe,QAAQ,GAAG,CAAC,CAAC,SAAS,UAAU;OACvD,QACM,CAEN;MACJ;MACA,MAAM,cAAc,SAAS,KAAK,UAAU,SAAS;MACrD,MAAM,eAAe,IAAI,MAAM,UAAU,qBAAqB,SAAS;MACvE,UAAU;MACV,aAAa;OACT,YAAY;OACZ,aAAa;MACjB;KACJ,GAAG,+DAA+D;KAClE,aAAa;MACT,iBAAiB;MACjB,gBAAgB;KACpB;IACJ;IACA,SAAS;IACT,MAAM,cAAc,SAAS,KAAK,UAAU,IAAI;IAChD,MAAM,eAAe,OAAO,UAAU,IAAI;IAC1C,KAAK;IACL,aAAa;KACT,SAAS,KAAA;KACT,YAAY;KACZ,eAAe;KACf,OAAO;IACX;GACJ,GAAG,4DAA4D;EACnE"}
|
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* dshell-conversation browser face —
|
|
2
|
+
* dshell-conversation browser face — the `terminal` conversation target.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* surface — no separate view tab — keeping PTY output and the input
|
|
11
|
-
* line fused in one column.
|
|
4
|
+
* The target is what takes a session out of dsh's centered hero phase and into
|
|
5
|
+
* the docked composer: ui-conversation resolves the shell phase from active
|
|
6
|
+
* targets, and its restore path falls back to `chat` whenever no preference
|
|
7
|
+
* exists. Registering it unconditionally (the old shape) forced EVERY session —
|
|
8
|
+
* including stock ones — into the docked layout, which is exactly the native
|
|
9
|
+
* surface this package must now leave alone.
|
|
12
10
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* precedence
|
|
11
|
+
* So the target, and the auto-activation that steers an opted-in session onto
|
|
12
|
+
* it, exist only while the CURRENT session carries the terminal-mode flag. The
|
|
13
|
+
* flag lives in dshell-mode's browser half and is reached by service key: a
|
|
14
|
+
* composition without it simply never registers the target.
|
|
15
|
+
*
|
|
16
|
+
* A subsequent `selectView` from the reader (the strip's 会话 tab) takes
|
|
17
|
+
* precedence through the stock path — activation only steers the default, and
|
|
18
|
+
* the strip's selection is a separate per-session preference.
|
|
20
19
|
*/
|
|
21
20
|
import type { Context } from '@deepseek-ai/cordis';
|
|
22
21
|
export declare const name = "@nexus-aethra/dshell-conversation/client";
|
|
23
22
|
export declare const inject: readonly ["uiConversation", "sessions", "slots"];
|
|
24
|
-
/** Register the
|
|
23
|
+
/** Register the `terminal` target and its activation only for opted-in sessions. */
|
|
25
24
|
export declare function apply(ctx: Context): void;
|
|
26
25
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAYlD,eAAO,MAAM,IAAI,6CAA6C,CAAA;AAE9D,eAAO,MAAM,MAAM,kDAAmD,CAAA;AAiCtE,oFAAoF;AACpF,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAuExC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexus-aethra/dshell-conversation",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "dshell Conversation activity marker: registers an always-active `terminal` view target so the conversation stays in active phase and the docked terminal surface renders instead of the centered hero.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,19 +33,19 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@nexus-aethra/dshell-std": "^0.1.
|
|
36
|
+
"@nexus-aethra/dshell-std": "^0.1.7"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
40
|
-
"@deepseek-ai/dsh-api-session-controller": "0.1.5-rc.2 || 0.1.6-alpha.2",
|
|
41
|
-
"@deepseek-ai/dsh-client-ui-conversation": "0.1.5-rc.2 || 0.1.6-alpha.2",
|
|
42
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2 || 0.1.6-alpha.2"
|
|
40
|
+
"@deepseek-ai/dsh-api-session-controller": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
|
|
41
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
|
|
42
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
46
|
-
"@deepseek-ai/dsh-api-session-controller": "0.1.5-rc.2 || 0.1.6-alpha.2",
|
|
47
|
-
"@deepseek-ai/dsh-client-ui-conversation": "0.1.5-rc.2 || 0.1.6-alpha.2",
|
|
48
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2 || 0.1.6-alpha.2",
|
|
46
|
+
"@deepseek-ai/dsh-api-session-controller": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
|
|
47
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
|
|
48
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2 || 0.1.6-alpha.2 || 0.1.7-rc.2",
|
|
49
49
|
"@types/react": "~18.3.1",
|
|
50
50
|
"@types/react-dom": "~18.3.0",
|
|
51
51
|
"react": "^18.2.0",
|