@linxin666/dsh-remote-web-ui 0.2.8 → 0.3.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/LICENSE +202 -28
- package/README.i18n.yaml +2 -2
- package/README.md +51 -15
- package/README.zh.md +9 -4
- package/lib/client.js +65 -34
- package/lib/client.js.map +1 -1
- package/lib/index.js +667 -77
- package/lib/mobile.js +874 -64
- package/lib/mobile.js.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +35 -5
- package/lib/types/client/remote-channel.d.ts +4 -5
- package/lib/types/client/remote-channel.d.ts.map +1 -1
- package/lib/types/client/remote-channel.js +13 -28
- package/lib/types/http.d.ts +37 -8
- package/lib/types/http.d.ts.map +1 -1
- package/lib/types/http.js +76 -11
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +40 -5
- package/lib/types/mobile/api.d.ts +57 -0
- package/lib/types/mobile/api.d.ts.map +1 -1
- package/lib/types/mobile/api.js +29 -0
- package/lib/types/mobile/markdown.d.ts +7 -1
- package/lib/types/mobile/markdown.d.ts.map +1 -1
- package/lib/types/mobile/markdown.js +16 -7
- package/lib/types/mobile/markdown.test.js +8 -0
- package/lib/types/mobile/mobile-styles.d.ts +1 -1
- package/lib/types/mobile/mobile-styles.d.ts.map +1 -1
- package/lib/types/mobile/mobile-styles.js +325 -6
- package/lib/types/mobile/views/ChatView.d.ts +10 -0
- package/lib/types/mobile/views/ChatView.d.ts.map +1 -1
- package/lib/types/mobile/views/ChatView.js +205 -8
- package/lib/types/mobile/views/ChatView.stream.test.d.ts +2 -0
- package/lib/types/mobile/views/ChatView.stream.test.d.ts.map +1 -0
- package/lib/types/mobile/views/ChatView.stream.test.js +205 -0
- package/lib/types/mobile/views/WorkspaceView.d.ts.map +1 -1
- package/lib/types/mobile/views/WorkspaceView.js +46 -5
- package/lib/types/mobile/views/WorkspaceView.test.js +129 -2
- package/lib/types/mobile-api.d.ts +9 -3
- package/lib/types/mobile-api.d.ts.map +1 -1
- package/lib/types/mobile-api.js +61 -16
- package/lib/types/mobile-pending.d.ts +38 -0
- package/lib/types/mobile-pending.d.ts.map +1 -0
- package/lib/types/mobile-pending.js +59 -0
- package/lib/types/paired-model-catalog.d.ts +17 -0
- package/lib/types/paired-model-catalog.d.ts.map +1 -0
- package/lib/types/paired-model-catalog.js +252 -0
- package/lib/types/pairing.d.ts +13 -4
- package/lib/types/pairing.d.ts.map +1 -1
- package/lib/types/pairing.js +13 -5
- package/lib/types/remote-api.d.ts +13 -4
- package/lib/types/remote-api.d.ts.map +1 -1
- package/lib/types/remote-api.js +27 -17
- package/lib/types/remote-channel-boot.d.ts +26 -0
- package/lib/types/remote-channel-boot.d.ts.map +1 -0
- package/lib/types/remote-channel-boot.js +128 -0
- package/lib/types/remote-channel-rules.d.ts +40 -0
- package/lib/types/remote-channel-rules.d.ts.map +1 -0
- package/lib/types/remote-channel-rules.js +32 -0
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +12 -23
- package/lib/types/uuid-polyfill.d.ts +10 -0
- package/lib/types/uuid-polyfill.d.ts.map +1 -0
- package/lib/types/uuid-polyfill.js +27 -0
- package/package.json +1 -1
- package/src/client/index.ts +41 -5
- package/src/client/remote-channel.ts +18 -28
- package/src/http.ts +87 -12
- package/src/index.ts +42 -6
- package/src/mobile/api.ts +91 -0
- package/src/mobile/markdown.test.ts +9 -0
- package/src/mobile/markdown.ts +17 -7
- package/src/mobile/mobile-styles.ts +326 -6
- package/src/mobile/views/ChatView.stream.test.tsx +231 -0
- package/src/mobile/views/ChatView.tsx +328 -10
- package/src/mobile/views/WorkspaceView.test.tsx +163 -2
- package/src/mobile/views/WorkspaceView.tsx +152 -26
- package/src/mobile-api.ts +59 -17
- package/src/mobile-pending.ts +88 -0
- package/src/paired-model-catalog.ts +224 -0
- package/src/pairing.ts +13 -5
- package/src/remote-api.ts +34 -18
- package/src/remote-channel-boot.ts +131 -0
- package/src/remote-channel-rules.ts +62 -0
- package/src/routes.ts +13 -24
- package/src/uuid-polyfill.ts +28 -0
package/src/mobile/api.ts
CHANGED
|
@@ -129,3 +129,94 @@ export async function selectModel(
|
|
|
129
129
|
...(selection.reasoningEffort !== undefined ? { reasoningEffort: selection.reasoningEffort } : {}),
|
|
130
130
|
})
|
|
131
131
|
}
|
|
132
|
+
|
|
133
|
+
/* ── pending approvals / questions (#1025) ───────────────────────────── */
|
|
134
|
+
|
|
135
|
+
/** One pending tool approval awaiting the user's decision. */
|
|
136
|
+
export interface PendingApproval {
|
|
137
|
+
approvalId: string
|
|
138
|
+
toolName: string
|
|
139
|
+
callId?: string
|
|
140
|
+
reason?: string
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** One pending question group awaiting the user's answer. */
|
|
144
|
+
export interface PendingQuestionItem {
|
|
145
|
+
id: string
|
|
146
|
+
question: string
|
|
147
|
+
detail?: string
|
|
148
|
+
header?: string
|
|
149
|
+
options?: Array<{ label: string; description?: string }>
|
|
150
|
+
multiSelect?: boolean
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** The pending state for one session. */
|
|
154
|
+
export interface PendingState {
|
|
155
|
+
approvals: PendingApproval[]
|
|
156
|
+
questions: PendingQuestionItem[]
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Fetch pending approvals and questions for one session (polling fallback data source). */
|
|
160
|
+
export async function fetchPending(sessionId: string): Promise<PendingState> {
|
|
161
|
+
return await callUnary<PendingState>('mobile.pending', { sessionId })
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Submit an approval decision (allowed-once or rejected). */
|
|
165
|
+
export async function respondApproval(
|
|
166
|
+
sessionId: string,
|
|
167
|
+
approvalId: string,
|
|
168
|
+
outcome: 'allowed-once' | 'rejected',
|
|
169
|
+
): Promise<void> {
|
|
170
|
+
await callUnary<unknown>('mobile.respond', {
|
|
171
|
+
sessionId,
|
|
172
|
+
type: 'approval',
|
|
173
|
+
approvalId,
|
|
174
|
+
outcome,
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Submit answers to a question group. */
|
|
179
|
+
export async function respondQuestion(
|
|
180
|
+
sessionId: string,
|
|
181
|
+
answers: Array<{ id: string; selected: string[]; custom?: string }>,
|
|
182
|
+
): Promise<void> {
|
|
183
|
+
await callUnary<unknown>('mobile.respond', {
|
|
184
|
+
sessionId,
|
|
185
|
+
type: 'question',
|
|
186
|
+
answers,
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* ── directory browsing / workspace creation (#977) ──────────────────── */
|
|
191
|
+
|
|
192
|
+
/** One entry in a directory listing. */
|
|
193
|
+
export interface DirectoryEntry {
|
|
194
|
+
name: string
|
|
195
|
+
path: string
|
|
196
|
+
hidden: boolean
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** The host directory listing result (one level, with ancestor breadcrumbs). */
|
|
200
|
+
export interface DirectoryListing {
|
|
201
|
+
path: string
|
|
202
|
+
home: string
|
|
203
|
+
crumbs: DirectoryEntry[]
|
|
204
|
+
entries: DirectoryEntry[]
|
|
205
|
+
truncated: boolean
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** The result of creating a workspace from an existing directory. */
|
|
209
|
+
export interface WorkspaceCreateResult {
|
|
210
|
+
workspace: WorkspaceView
|
|
211
|
+
created: boolean
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Browse one directory level on the host (defaults to home directory). */
|
|
215
|
+
export async function listDirectory(path?: string): Promise<DirectoryListing> {
|
|
216
|
+
return await callUnary<DirectoryListing>('host.listDirectory', path === undefined ? {} : { path })
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Create a workspace from an existing host directory (does not mkdir). */
|
|
220
|
+
export async function createWorkspace(path: string): Promise<WorkspaceCreateResult> {
|
|
221
|
+
return await callUnary<WorkspaceCreateResult>('workspace.create', { path })
|
|
222
|
+
}
|
|
@@ -79,4 +79,13 @@ describe('escapeHtml', () => {
|
|
|
79
79
|
it('escapes all five HTML-significant characters', () => {
|
|
80
80
|
expect(escapeHtml('<&>"\'')).toBe('<&>"'')
|
|
81
81
|
})
|
|
82
|
+
|
|
83
|
+
it('equals the five sequential passes on mixed, pre-escaped and plain input', () => {
|
|
84
|
+
// Single-pass map replacement must never diverge from the sequential
|
|
85
|
+
// five-pass escape (including double-escaping of literal entities).
|
|
86
|
+
expect(escapeHtml('a & b < c > d " e \' f')).toBe('a & b < c > d " e ' f')
|
|
87
|
+
expect(escapeHtml('& < >')).toBe('&amp; &lt; &gt;')
|
|
88
|
+
expect(escapeHtml('plain text 123')).toBe('plain text 123')
|
|
89
|
+
expect(escapeHtml('')).toBe('')
|
|
90
|
+
})
|
|
82
91
|
})
|
package/src/mobile/markdown.ts
CHANGED
|
@@ -10,14 +10,24 @@
|
|
|
10
10
|
* @module dsh-remote-web-ui/mobile/markdown
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
/**
|
|
13
|
+
/** HTML special-character map for {@link escapeHtml}. */
|
|
14
|
+
const HTML_ESCAPE_MAP: Record<string, string> = {
|
|
15
|
+
'&': '&',
|
|
16
|
+
'<': '<',
|
|
17
|
+
'>': '>',
|
|
18
|
+
'"': '"',
|
|
19
|
+
"'": ''',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Escape HTML special characters. One regex pass replaces all five
|
|
24
|
+
* characters through the map; the output is identical to five sequential
|
|
25
|
+
* passes (each replacement string contains none of the escaped characters),
|
|
26
|
+
* and the common no-special-character case scans the string only once
|
|
27
|
+
* instead of five times.
|
|
28
|
+
*/
|
|
14
29
|
export function escapeHtml(text: string): string {
|
|
15
|
-
return text
|
|
16
|
-
.replace(/&/g, '&')
|
|
17
|
-
.replace(/</g, '<')
|
|
18
|
-
.replace(/>/g, '>')
|
|
19
|
-
.replace(/"/g, '"')
|
|
20
|
-
.replace(/'/g, ''')
|
|
30
|
+
return text.replace(/[&<>"']/g, char => HTML_ESCAPE_MAP[char] ?? char)
|
|
21
31
|
}
|
|
22
32
|
|
|
23
33
|
/**
|
|
@@ -588,16 +588,33 @@ body {
|
|
|
588
588
|
gap: 8px;
|
|
589
589
|
}
|
|
590
590
|
|
|
591
|
-
.chat-tool-
|
|
591
|
+
.chat-tool-card {
|
|
592
592
|
display: flex;
|
|
593
593
|
flex-direction: column;
|
|
594
|
-
gap:
|
|
594
|
+
gap: 4px;
|
|
595
|
+
padding: 6px 8px;
|
|
596
|
+
border: 1px solid var(--m-border);
|
|
597
|
+
border-radius: 8px;
|
|
598
|
+
background: var(--m-bg-raised);
|
|
595
599
|
}
|
|
596
600
|
|
|
597
|
-
.chat-tool-
|
|
598
|
-
|
|
599
|
-
|
|
601
|
+
.chat-tool-pills {
|
|
602
|
+
display: flex;
|
|
603
|
+
gap: 4px;
|
|
604
|
+
flex-wrap: wrap;
|
|
605
|
+
align-items: center;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.chat-tool-pill {
|
|
609
|
+
display: inline-block;
|
|
610
|
+
padding: 1px 7px;
|
|
611
|
+
border-radius: 999px;
|
|
612
|
+
background: var(--m-accent-soft);
|
|
613
|
+
color: var(--m-accent);
|
|
614
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
615
|
+
font-size: 11px;
|
|
600
616
|
font-weight: 600;
|
|
617
|
+
white-space: nowrap;
|
|
601
618
|
}
|
|
602
619
|
|
|
603
620
|
.chat-tool-args {
|
|
@@ -611,8 +628,245 @@ body {
|
|
|
611
628
|
line-height: 1.5;
|
|
612
629
|
overflow-wrap: break-word;
|
|
613
630
|
white-space: pre-wrap;
|
|
631
|
+
max-height: 200px;
|
|
632
|
+
overflow-y: auto;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/* ── turn status indicator (#1017) ───────────────────────────────────── */
|
|
636
|
+
|
|
637
|
+
.chat-turn-status {
|
|
638
|
+
display: flex;
|
|
639
|
+
align-items: center;
|
|
640
|
+
gap: 6px;
|
|
641
|
+
padding: 10px 16px;
|
|
642
|
+
color: var(--m-accent);
|
|
643
|
+
font-size: 13px;
|
|
644
|
+
font-weight: 600;
|
|
645
|
+
background: linear-gradient(
|
|
646
|
+
90deg,
|
|
647
|
+
var(--m-accent) 0%,
|
|
648
|
+
color-mix(in srgb, var(--m-accent) 60%, #ffffff) 50%,
|
|
649
|
+
var(--m-accent) 100%
|
|
650
|
+
);
|
|
651
|
+
background-size: 200% 100%;
|
|
652
|
+
background-clip: text;
|
|
653
|
+
-webkit-background-clip: text;
|
|
654
|
+
-webkit-text-fill-color: transparent;
|
|
655
|
+
animation: dsh-shimmer 1.8s linear infinite;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
.chat-turn-dots {
|
|
659
|
+
display: inline-flex;
|
|
660
|
+
gap: 3px;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.chat-turn-dots span {
|
|
664
|
+
display: inline-block;
|
|
665
|
+
width: 5px;
|
|
666
|
+
height: 5px;
|
|
667
|
+
border-radius: 50%;
|
|
668
|
+
background: var(--m-accent);
|
|
669
|
+
animation: dsh-dot-bounce 1.2s ease-in-out infinite;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.chat-turn-dots span:nth-child(2) {
|
|
673
|
+
animation-delay: 0.15s;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
.chat-turn-dots span:nth-child(3) {
|
|
677
|
+
animation-delay: 0.3s;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
@keyframes dsh-shimmer {
|
|
681
|
+
from { background-position: 200% 0; }
|
|
682
|
+
to { background-position: -200% 0; }
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
@keyframes dsh-dot-bounce {
|
|
686
|
+
0%, 80%, 100% { transform: translateY(0); opacity: 0.4; }
|
|
687
|
+
40% { transform: translateY(-4px); opacity: 1; }
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/* ── approval panel (#1025) ──────────────────────────────────────────── */
|
|
691
|
+
|
|
692
|
+
.chat-approval-panel {
|
|
693
|
+
margin: 8px 12px;
|
|
694
|
+
padding: 10px 12px;
|
|
695
|
+
border: 1px solid var(--m-accent);
|
|
696
|
+
border-radius: var(--m-radius);
|
|
697
|
+
background: var(--m-bg-raised);
|
|
698
|
+
box-shadow: var(--m-shadow);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
.chat-approval-header {
|
|
702
|
+
display: flex;
|
|
703
|
+
align-items: center;
|
|
704
|
+
gap: 8px;
|
|
705
|
+
margin-bottom: 8px;
|
|
614
706
|
}
|
|
615
707
|
|
|
708
|
+
.chat-approval-reason {
|
|
709
|
+
color: var(--m-text-secondary);
|
|
710
|
+
font-size: 12.5px;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.chat-approval-error {
|
|
714
|
+
margin: 4px 0;
|
|
715
|
+
color: var(--m-danger);
|
|
716
|
+
font-size: 12px;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.chat-approval-actions {
|
|
720
|
+
display: flex;
|
|
721
|
+
gap: 8px;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
.chat-approval-allow {
|
|
725
|
+
flex: 1;
|
|
726
|
+
height: 34px;
|
|
727
|
+
border: none;
|
|
728
|
+
border-radius: 8px;
|
|
729
|
+
background: var(--m-accent);
|
|
730
|
+
color: #fff;
|
|
731
|
+
font: inherit;
|
|
732
|
+
font-size: 13px;
|
|
733
|
+
font-weight: 600;
|
|
734
|
+
cursor: pointer;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
.chat-approval-allow:disabled {
|
|
738
|
+
opacity: 0.6;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
.chat-approval-reject {
|
|
742
|
+
flex: 1;
|
|
743
|
+
height: 34px;
|
|
744
|
+
border: 1px solid var(--m-border);
|
|
745
|
+
border-radius: 8px;
|
|
746
|
+
background: var(--m-bg-raised);
|
|
747
|
+
color: var(--m-text);
|
|
748
|
+
font: inherit;
|
|
749
|
+
font-size: 13px;
|
|
750
|
+
cursor: pointer;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
.chat-approval-reject:disabled {
|
|
754
|
+
opacity: 0.6;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/* ── question panel (#1025) ──────────────────────────────────────────── */
|
|
758
|
+
|
|
759
|
+
.chat-question-panel {
|
|
760
|
+
margin: 8px 12px;
|
|
761
|
+
padding: 10px 12px;
|
|
762
|
+
border: 1px solid var(--m-accent);
|
|
763
|
+
border-radius: var(--m-radius);
|
|
764
|
+
background: var(--m-bg-raised);
|
|
765
|
+
box-shadow: var(--m-shadow);
|
|
766
|
+
display: flex;
|
|
767
|
+
flex-direction: column;
|
|
768
|
+
gap: 10px;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.chat-question-group {
|
|
772
|
+
display: flex;
|
|
773
|
+
flex-direction: column;
|
|
774
|
+
gap: 6px;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.chat-question-header {
|
|
778
|
+
font-size: 14px;
|
|
779
|
+
font-weight: 600;
|
|
780
|
+
color: var(--m-text);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.chat-question-text {
|
|
784
|
+
font-size: 13px;
|
|
785
|
+
color: var(--m-text);
|
|
786
|
+
line-height: 1.5;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
.chat-question-detail {
|
|
790
|
+
font-size: 12px;
|
|
791
|
+
color: var(--m-text-secondary);
|
|
792
|
+
line-height: 1.5;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
.chat-question-options {
|
|
796
|
+
display: flex;
|
|
797
|
+
flex-direction: column;
|
|
798
|
+
gap: 4px;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
.chat-question-option {
|
|
802
|
+
display: flex;
|
|
803
|
+
align-items: flex-start;
|
|
804
|
+
gap: 8px;
|
|
805
|
+
padding: 8px 10px;
|
|
806
|
+
border: 1px solid var(--m-border);
|
|
807
|
+
border-radius: 8px;
|
|
808
|
+
cursor: pointer;
|
|
809
|
+
font-size: 13px;
|
|
810
|
+
transition: border-color 0.12s, background 0.12s;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
.chat-question-option input {
|
|
814
|
+
margin: 2px 0 0;
|
|
815
|
+
flex: none;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
.chat-question-option-selected {
|
|
819
|
+
border-color: var(--m-accent);
|
|
820
|
+
background: var(--m-accent-soft);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.chat-question-option-label {
|
|
824
|
+
color: var(--m-text);
|
|
825
|
+
font-weight: 500;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
.chat-question-option-desc {
|
|
829
|
+
display: block;
|
|
830
|
+
color: var(--m-text-tertiary);
|
|
831
|
+
font-size: 12px;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
.chat-question-custom {
|
|
835
|
+
width: 100%;
|
|
836
|
+
min-height: 40px;
|
|
837
|
+
box-sizing: border-box;
|
|
838
|
+
padding: 8px 10px;
|
|
839
|
+
border: 1px solid var(--m-border);
|
|
840
|
+
border-radius: 8px;
|
|
841
|
+
background: var(--m-bg-input);
|
|
842
|
+
color: var(--m-text);
|
|
843
|
+
font: inherit;
|
|
844
|
+
font-size: 13px;
|
|
845
|
+
resize: vertical;
|
|
846
|
+
outline: none;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.chat-question-custom:focus-visible {
|
|
850
|
+
border-color: var(--m-accent);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
.chat-question-submit {
|
|
854
|
+
height: 36px;
|
|
855
|
+
border: none;
|
|
856
|
+
border-radius: 8px;
|
|
857
|
+
background: var(--m-accent);
|
|
858
|
+
color: #fff;
|
|
859
|
+
font: inherit;
|
|
860
|
+
font-size: 13px;
|
|
861
|
+
font-weight: 600;
|
|
862
|
+
cursor: pointer;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
.chat-question-submit:disabled {
|
|
866
|
+
opacity: 0.6;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
|
|
616
870
|
/* ── composer toolbar (model / permission chips) ─────────────────────── */
|
|
617
871
|
|
|
618
872
|
.chat-tools {
|
|
@@ -1053,10 +1307,16 @@ body {
|
|
|
1053
1307
|
.sheet-confirm-danger,
|
|
1054
1308
|
.sheet-toggle-switch,
|
|
1055
1309
|
.sheet-toggle-switch-knob,
|
|
1056
|
-
.chat-input
|
|
1310
|
+
.chat-input,
|
|
1311
|
+
.chat-turn-status,
|
|
1312
|
+
.chat-turn-dots span {
|
|
1057
1313
|
animation: none;
|
|
1058
1314
|
transition: none;
|
|
1059
1315
|
}
|
|
1316
|
+
.chat-turn-status {
|
|
1317
|
+
background: none;
|
|
1318
|
+
-webkit-text-fill-color: var(--m-accent);
|
|
1319
|
+
}
|
|
1060
1320
|
}
|
|
1061
1321
|
|
|
1062
1322
|
/* ── installed-app pairing ───────────────────────────────────────────── */
|
|
@@ -1106,4 +1366,64 @@ body {
|
|
|
1106
1366
|
.mobile-pairSubmit {
|
|
1107
1367
|
width: 100%;
|
|
1108
1368
|
}
|
|
1369
|
+
|
|
1370
|
+
/* ── directory browser ─────────────────────────────────────────────── */
|
|
1371
|
+
|
|
1372
|
+
.dir-browser {
|
|
1373
|
+
display: flex;
|
|
1374
|
+
flex-direction: column;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
.dir-crumbs {
|
|
1378
|
+
display: flex;
|
|
1379
|
+
align-items: center;
|
|
1380
|
+
overflow-x: auto;
|
|
1381
|
+
padding: 12px 16px;
|
|
1382
|
+
background: var(--m-bg-raised);
|
|
1383
|
+
border-bottom: 1px solid var(--m-border);
|
|
1384
|
+
white-space: nowrap;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
.dir-crumbs::-webkit-scrollbar {
|
|
1388
|
+
display: none;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
.dir-crumb {
|
|
1392
|
+
border: none;
|
|
1393
|
+
background: transparent;
|
|
1394
|
+
padding: 4px 6px;
|
|
1395
|
+
color: var(--m-text);
|
|
1396
|
+
font-size: 14px;
|
|
1397
|
+
cursor: pointer;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
.dir-crumb:active {
|
|
1401
|
+
background: var(--m-bg-input);
|
|
1402
|
+
border-radius: 4px;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
.dir-crumb-separator {
|
|
1406
|
+
color: var(--m-text-tertiary);
|
|
1407
|
+
margin: 0 4px;
|
|
1408
|
+
font-size: 14px;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
.dir-entry {
|
|
1412
|
+
padding-left: 16px;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
.dir-entry-hidden {
|
|
1416
|
+
opacity: 0.5;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
.dir-select {
|
|
1420
|
+
padding: 16px;
|
|
1421
|
+
background: var(--m-bg);
|
|
1422
|
+
border-top: 1px solid var(--m-border);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
.dir-empty {
|
|
1426
|
+
padding: 40px 0;
|
|
1427
|
+
}
|
|
1109
1428
|
`
|
|
1429
|
+
|