dsh-ntr 0.1.1 → 0.1.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/lib/client/acp-ui.js +61 -0
- package/lib/client/index.js +13 -0
- package/lib/client/panel.js +48 -12
- package/lib/client/session-controls.js +59 -13
- package/lib/client/settings-dialog.js +43 -0
- package/lib/client/store.js +2 -0
- package/lib/client.js +276 -28
- package/lib/client.js.map +1 -1
- package/lib/types/client/acp-ui.d.ts +8 -0
- package/lib/types/client/session-controls.d.ts +27 -6
- package/lib/types/client/settings-dialog.d.ts +23 -0
- package/lib/types/client/store.d.ts +6 -0
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +2 -2
- package/src/client/acp-ui.tsx +68 -0
- package/src/client/index.tsx +14 -0
- package/src/client/panel.tsx +84 -13
- package/src/client/session-controls.tsx +71 -15
- package/src/client/settings-dialog.tsx +73 -0
- package/src/client/store.ts +8 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The NTR settings dialog, opened from the agent base's empty-roster menu.
|
|
3
|
+
*
|
|
4
|
+
* WHY A DIALOG OF OUR OWN: the host settings shell owns its open state and
|
|
5
|
+
* exposes no programmatic way in. The sidebar seat that receives `openSettings`
|
|
6
|
+
* (`settings.launcher`) is a single-occupant slot already held by the account
|
|
7
|
+
* menu, and `openSection` is only handed to `settings.onboarding` registrants —
|
|
8
|
+
* so a conversation surface cannot drive the shell's modal. This dialog mounts
|
|
9
|
+
* the SAME `AcpSettingsSection` the shell mounts, on the frame's overlay layer
|
|
10
|
+
* (`shell.overlay`): one settings surface, two entrances, no duplicated
|
|
11
|
+
* configuration logic.
|
|
12
|
+
*
|
|
13
|
+
* The trigger (composer model seat) and this dialog (root overlay) live in
|
|
14
|
+
* unrelated slot trees, so the open state is the module-level store in
|
|
15
|
+
* `acp-ui.tsx` (`openAcpSettingsDialog`).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { useEffect } from 'react'
|
|
19
|
+
import { AcpSettingsSection } from './panel.tsx'
|
|
20
|
+
import {
|
|
21
|
+
closeAcpSettingsDialog,
|
|
22
|
+
ensureAcpStyle,
|
|
23
|
+
useAcpSettingsDialogOpen,
|
|
24
|
+
} from './acp-ui.tsx'
|
|
25
|
+
import type { AcpStore } from './store.ts'
|
|
26
|
+
|
|
27
|
+
/** The root overlay entry's inject face. */
|
|
28
|
+
export interface AcpSettingsDialogProps {
|
|
29
|
+
acp: AcpStore
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Frame-wide NTR settings surface; renders nothing while closed. */
|
|
33
|
+
export function AcpSettingsDialog(props: AcpSettingsDialogProps) {
|
|
34
|
+
ensureAcpStyle()
|
|
35
|
+
const { acp } = props
|
|
36
|
+
const open = useAcpSettingsDialogOpen()
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (!open) return
|
|
39
|
+
const onKey = (event: KeyboardEvent): void => {
|
|
40
|
+
if (event.key !== 'Escape') return
|
|
41
|
+
event.preventDefault()
|
|
42
|
+
event.stopPropagation()
|
|
43
|
+
closeAcpSettingsDialog()
|
|
44
|
+
}
|
|
45
|
+
document.addEventListener('keydown', onKey, true)
|
|
46
|
+
return () => { document.removeEventListener('keydown', onKey, true) }
|
|
47
|
+
}, [open])
|
|
48
|
+
if (!open) return null
|
|
49
|
+
const title = acp.label('NTR 设置', 'NTR settings')
|
|
50
|
+
return (
|
|
51
|
+
<div className="ntr-acp-dialog-layer">
|
|
52
|
+
<div className="ntr-acp-dialog-mask" aria-hidden onClick={closeAcpSettingsDialog} />
|
|
53
|
+
<div className="ntr-acp-dialog" role="dialog" aria-modal="true" aria-label={title}>
|
|
54
|
+
<div className="ntr-acp-dialog-head">
|
|
55
|
+
<span className="ntr-acp-dialog-title">{title}</span>
|
|
56
|
+
<button
|
|
57
|
+
type="button"
|
|
58
|
+
className="ntr-acp-dialog-close"
|
|
59
|
+
title={acp.label('关闭', 'Close')}
|
|
60
|
+
aria-label={acp.label('关闭 NTR 设置', 'Close NTR settings')}
|
|
61
|
+
autoFocus
|
|
62
|
+
onClick={closeAcpSettingsDialog}
|
|
63
|
+
>
|
|
64
|
+
✕
|
|
65
|
+
</button>
|
|
66
|
+
</div>
|
|
67
|
+
<div className="ntr-acp-dialog-body">
|
|
68
|
+
<AcpSettingsSection acp={acp} />
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
)
|
|
73
|
+
}
|
package/src/client/store.ts
CHANGED
|
@@ -91,6 +91,12 @@ export interface UiState {
|
|
|
91
91
|
localeRev: number
|
|
92
92
|
stream: 'init' | 'live' | 'down'
|
|
93
93
|
agents: AgentView[]
|
|
94
|
+
/**
|
|
95
|
+
* Whether the host has answered `agents/list` at least once in this page.
|
|
96
|
+
* `agents: []` alone cannot tell "no agent configured" from "not read yet";
|
|
97
|
+
* a surface that offers a setup entry on an empty roster must wait for this.
|
|
98
|
+
*/
|
|
99
|
+
agentsLoaded: boolean
|
|
94
100
|
sessions: SessionView[]
|
|
95
101
|
activeSessionId?: string
|
|
96
102
|
blocks: Block[]
|
|
@@ -515,6 +521,7 @@ export class AcpStore {
|
|
|
515
521
|
localeRev: 0,
|
|
516
522
|
stream: 'init',
|
|
517
523
|
agents: [],
|
|
524
|
+
agentsLoaded: false,
|
|
518
525
|
sessions: [],
|
|
519
526
|
blocks: [],
|
|
520
527
|
permissions: [],
|
|
@@ -744,6 +751,7 @@ export class AcpStore {
|
|
|
744
751
|
// Absent on an older host: keep the settings-panel card (degraded path).
|
|
745
752
|
this.mutate({
|
|
746
753
|
agents,
|
|
754
|
+
agentsLoaded: true,
|
|
747
755
|
sessions,
|
|
748
756
|
defaultAgent: defaultAgentInfo?.agentId,
|
|
749
757
|
detectedAgents,
|