@tiagojct/subsub 0.5.0 → 0.6.1

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/web/app.css ADDED
@@ -0,0 +1,256 @@
1
+ /* Sub-Sub web view. Glauca tokens (glauca.css); the librarian gets the Try-Works orange. */
2
+
3
+ @font-face { font-family: "IBM Plex Sans"; src: url("/fonts/IBMPlexSans.woff2") format("woff2"); font-weight: 100 700; font-display: swap; }
4
+ @font-face { font-family: "IBM Plex Serif"; src: url("/fonts/IBMPlexSerif.woff2") format("woff2"); font-weight: 100 700; font-display: swap; }
5
+ @font-face { font-family: "IBM Plex Mono"; src: url("/fonts/IBMPlexMono.woff2") format("woff2"); font-weight: 100 700; font-display: swap; }
6
+
7
+ :root {
8
+ --mode: var(--gl-accent);
9
+ --mode-deep: var(--gl-accent-deep);
10
+ --on-mode: var(--gl-on-accent);
11
+ --add: #1d7a3a;
12
+ --rem: #b3261e;
13
+ --warn: #8a5a00;
14
+ --side-w: 17rem;
15
+ }
16
+ @media (prefers-color-scheme: dark) {
17
+ :root:not([data-theme="light"]) {
18
+ --gl-bg: #10161c;
19
+ --gl-surface: #171f26;
20
+ --gl-surface-raised: #1f2932;
21
+ --gl-text: #e8eef2;
22
+ --gl-text-muted: #9aa4ab;
23
+ --gl-border: #2a3540;
24
+ --gl-accent: #3d97ff;
25
+ --gl-accent-bright: #6cb2ff;
26
+ --gl-accent-deep: #007aff;
27
+ --gl-on-accent: #0b1218;
28
+ --gl-tint-pale: #93b7c9;
29
+ --add: #8fcf9a;
30
+ --rem: #f0a39c;
31
+ --warn: #e3b35c;
32
+ }
33
+ }
34
+ [data-theme="dark"], html[data-theme="dark"] {
35
+ --gl-bg: #10161c;
36
+ --gl-surface: #171f26;
37
+ --gl-surface-raised: #1f2932;
38
+ --gl-text: #e8eef2;
39
+ --gl-text-muted: #9aa4ab;
40
+ --gl-border: #2a3540;
41
+ --gl-accent: #3d97ff;
42
+ --gl-accent-bright: #6cb2ff;
43
+ --gl-accent-deep: #007aff;
44
+ --gl-on-accent: #0b1218;
45
+ --gl-tint-pale: #93b7c9;
46
+ --add: #8fcf9a;
47
+ --rem: #f0a39c;
48
+ --warn: #e3b35c;
49
+ }
50
+ body[data-mode="librarian"] { --mode: #a8520f; --mode-deep: #7f3d09; --on-mode: #fff8f0; }
51
+ @media (prefers-color-scheme: dark) {
52
+ :root:not([data-theme="light"]) body[data-mode="librarian"] { --mode: #e0832a; --mode-deep: #f09a48; --on-mode: #140c04; }
53
+ }
54
+ [data-theme="dark"] body[data-mode="librarian"], html[data-theme="dark"] body[data-mode="librarian"] {
55
+ --mode: #e0832a; --mode-deep: #f09a48; --on-mode: #140c04;
56
+ }
57
+
58
+ *, *::before, *::after { box-sizing: border-box; }
59
+ html, body { height: 100%; }
60
+ body {
61
+ margin: 0; background: var(--gl-bg); color: var(--gl-text);
62
+ font-family: var(--gl-font-sans); font-size: 1rem; line-height: 1.55;
63
+ transition: background-color 0.2s ease, color 0.2s ease;
64
+ }
65
+ button, input, select, textarea { font: inherit; color: inherit; }
66
+ a { color: var(--gl-accent); }
67
+ .visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
68
+ [hidden] { display: none !important; }
69
+ :focus-visible { outline: 2px solid var(--mode); outline-offset: 2px; }
70
+
71
+ /* ---- buttons */
72
+ button { cursor: pointer; border-radius: var(--gl-radius-md); border: 1px solid transparent; padding: 0.45rem 0.9rem; background: none; }
73
+ button:disabled { opacity: 0.5; cursor: default; }
74
+ .primary { background: var(--mode); color: var(--on-mode); font-weight: 600; }
75
+ .primary:hover:not(:disabled) { background: var(--mode-deep); }
76
+ .secondary { border-color: var(--gl-border); background: var(--gl-surface-raised); }
77
+ .secondary:hover:not(:disabled) { border-color: var(--mode); }
78
+ .ghost { color: var(--gl-text-muted); }
79
+ .ghost:hover { color: var(--gl-text); background: var(--gl-surface); }
80
+ .small { font-size: 0.85rem; }
81
+ .muted { color: var(--gl-text-muted); }
82
+ .block { width: 100%; }
83
+
84
+ /* ---- layout */
85
+ .app { display: grid; grid-template-columns: var(--side-w) minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); height: 100vh; height: 100dvh; }
86
+ .top {
87
+ grid-column: 1 / -1; display: flex; align-items: center; gap: 0.75rem 1.25rem; flex-wrap: wrap;
88
+ padding: 0.6rem 1rem; border-bottom: 1px solid var(--gl-border); background: var(--gl-surface);
89
+ }
90
+ .brand { font-family: var(--gl-font-serif); font-weight: 600; font-size: 1.15rem; display: flex; align-items: center; gap: 0.45rem; }
91
+ .mark { font-family: var(--gl-font-mono); font-size: 0.75rem; font-weight: 600; background: #12161b; color: #e0832a; padding: 0.15rem 0.35rem; border-radius: var(--gl-radius-md); }
92
+ .menu-btn { display: none; }
93
+ .modes { display: inline-flex; border: 1px solid var(--gl-border); border-radius: var(--gl-radius-pill); padding: 2px; background: var(--gl-bg); }
94
+ .modes button { border-radius: var(--gl-radius-pill); padding: 0.3rem 0.9rem; color: var(--gl-text-muted); }
95
+ .modes button[aria-pressed="true"] { background: var(--mode); color: var(--on-mode); font-weight: 600; }
96
+ .status { margin-left: auto; display: flex; align-items: center; gap: 0.5rem 0.9rem; flex-wrap: wrap; font-size: 0.88rem; }
97
+ .library { display: inline-flex; align-items: center; gap: 0.45rem; color: var(--gl-text-muted); }
98
+ .library-dot { width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--gl-border); flex: none; transition: background 0.2s ease, box-shadow 0.2s ease; }
99
+ .library.reachable .library-dot { background: var(--add); box-shadow: 0 0 6px var(--add); }
100
+ .library.down .library-dot { background: var(--rem); box-shadow: 0 0 6px var(--rem); }
101
+ .library.down { color: var(--rem); font-weight: 600; }
102
+ .library.checking .library-dot { background: var(--warn); animation: pulse 1.2s ease-in-out infinite; }
103
+ .status select { border: 1px solid var(--gl-border); border-radius: var(--gl-radius-md); background: var(--gl-surface-raised); padding: 0.3rem 0.5rem; }
104
+ .model-btn, .theme-btn { font-family: var(--gl-font-mono); font-size: 0.8rem; border: 1px solid var(--gl-border); }
105
+ .theme-btn { display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.3rem 0.6rem; }
106
+
107
+ .side { border-right: 1px solid var(--gl-border); background: var(--gl-surface); padding: 1rem; display: flex; flex-direction: column; gap: 1.25rem; overflow-y: auto; }
108
+ .side h2 { font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--gl-text-muted); margin: 0 0 0.5rem; font-weight: 600; }
109
+ .side .grow { flex: 1; display: flex; flex-direction: column; min-height: 0; }
110
+ .side-subhead { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.4rem; }
111
+ .side-subhead h2 { margin: 0; }
112
+ .search-wrap { margin-bottom: 0.6rem; }
113
+ .search-wrap input { width: 100%; font-size: 0.8rem; padding: 0.35rem 0.55rem; border: 1px solid var(--gl-border); border-radius: var(--gl-radius-md); background: var(--gl-surface-raised); color: var(--gl-text); }
114
+ .search-wrap input:focus { outline: none; border-color: var(--mode); }
115
+ .actions { display: flex; flex-direction: column; gap: 0.25rem; }
116
+ .actions button { text-align: left; padding: 0.35rem 0.6rem; border-radius: var(--gl-radius-md); }
117
+ .actions button:hover { background: var(--gl-bg); }
118
+ .actions hr { border: 0; border-top: 1px solid var(--gl-border); margin: 0.35rem 0; width: 100%; }
119
+ .sessions { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.15rem; overflow-y: auto; flex: 1; }
120
+ .sessions button { width: 100%; text-align: left; padding: 0.4rem 0.6rem; display: block; border-radius: var(--gl-radius-md); }
121
+ .sessions button:hover { background: var(--gl-bg); }
122
+ .sessions .current button { background: var(--gl-bg); border-color: var(--gl-border); }
123
+ .sessions .when { display: flex; justify-content: space-between; font-size: 0.75rem; color: var(--gl-text-muted); margin-top: 0.15rem; }
124
+ .sessions .badge { font-family: var(--gl-font-mono); font-size: 0.7rem; background: var(--gl-surface-raised); border: 1px solid var(--gl-border); border-radius: var(--gl-radius-sm); padding: 0 0.3rem; }
125
+ .sessions .what { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; font-size: 0.88rem; }
126
+ .side-foot { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; }
127
+ .version { font-size: 0.75rem; color: var(--gl-text-muted); font-family: var(--gl-font-mono); }
128
+
129
+ /* ---- conversation */
130
+ .chat { display: flex; flex-direction: column; min-height: 0; }
131
+ .banner { margin: 0.75rem 1rem 0; padding: 0.6rem 0.9rem; border: 1px solid var(--gl-border); border-left: 3px solid var(--warn); border-radius: var(--gl-radius-md); background: var(--gl-surface); display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap; }
132
+ .log { flex: 1; overflow-y: auto; padding: 1.25rem 1rem 2rem; }
133
+ .log > * { max-width: 48rem; margin-left: auto; margin-right: auto; }
134
+ .msg { margin-top: 1.1rem; }
135
+ .msg.user { display: flex; justify-content: flex-end; }
136
+ .msg.user .bubble { background: var(--gl-surface-raised); border: 1px solid var(--gl-border); border-radius: 12px 12px 3px 12px; padding: 0.55rem 0.9rem; max-width: 85%; white-space: pre-wrap; overflow-wrap: anywhere; }
137
+ .msg.assistant .md { overflow-wrap: anywhere; }
138
+ .md > :first-child { margin-top: 0; }
139
+ .md > :last-child { margin-bottom: 0; }
140
+ .md p, .md ul, .md ol { margin: 0.6rem 0; }
141
+ .md h1, .md h2, .md h3 { font-family: var(--gl-font-serif); line-height: 1.25; margin: 1.1rem 0 0.4rem; }
142
+ .md h1 { font-size: 1.35rem; } .md h2 { font-size: 1.2rem; } .md h3 { font-size: 1.05rem; }
143
+ .md code { font-family: var(--gl-font-mono); font-size: 0.86em; background: var(--gl-surface); border: 1px solid var(--gl-border); border-radius: var(--gl-radius-sm); padding: 0.05em 0.3em; }
144
+ .md pre { background: var(--gl-surface); border: 1px solid var(--gl-border); border-radius: var(--gl-radius-md); padding: 0.75rem; overflow-x: auto; font-size: 0.85rem; }
145
+ .md pre code { background: none; border: 0; padding: 0; }
146
+ .pre-wrap { position: relative; margin: 0.6rem 0; }
147
+ .pre-wrap pre { margin: 0; }
148
+ .copy-btn {
149
+ position: absolute; top: 0.4rem; right: 0.4rem;
150
+ font-family: var(--gl-font-mono); font-size: 0.72rem;
151
+ background: var(--gl-surface-raised); border: 1px solid var(--gl-border);
152
+ border-radius: var(--gl-radius-sm); padding: 0.15rem 0.5rem;
153
+ color: var(--gl-text-muted); cursor: pointer; opacity: 0;
154
+ transition: opacity 0.15s ease, color 0.15s ease, border-color 0.15s ease;
155
+ z-index: 2;
156
+ }
157
+ .pre-wrap:hover .copy-btn, .copy-btn:focus-visible { opacity: 1; }
158
+ .copy-btn:hover { color: var(--gl-text); border-color: var(--mode); }
159
+ .copy-btn.copied { opacity: 1; color: var(--add); border-color: var(--add); }
160
+
161
+ .msg-actions { display: flex; gap: 0.4rem; margin-top: 0.45rem; opacity: 0; transition: opacity 0.15s ease; }
162
+ .msg.assistant:hover .msg-actions, .msg-actions:focus-within { opacity: 1; }
163
+ .msg-action-btn { font-size: 0.75rem; padding: 0.15rem 0.5rem; border-radius: var(--gl-radius-sm); color: var(--gl-text-muted); border: 1px solid transparent; background: none; }
164
+ .msg-action-btn:hover { color: var(--gl-text); background: var(--gl-surface-raised); border-color: var(--gl-border); }
165
+ .msg-action-btn.copied { color: var(--add); border-color: var(--add); }
166
+
167
+ .md blockquote { margin: 0.6rem 0; padding-left: 0.9rem; border-left: 3px solid var(--gl-tint-pale); color: var(--gl-text-muted); }
168
+ .md table { border-collapse: collapse; display: block; overflow-x: auto; font-size: 0.9rem; }
169
+ .md th, .md td { border-bottom: 1px solid var(--gl-border); padding: 0.3rem 0.6rem; text-align: left; vertical-align: top; }
170
+ .md th { font-size: 0.82rem; color: var(--gl-text-muted); }
171
+ .tool { margin-top: 0.5rem; font-size: 0.86rem; color: var(--gl-text-muted); }
172
+ .tool summary { cursor: pointer; list-style: none; display: flex; gap: 0.5rem; align-items: baseline; flex-wrap: wrap; }
173
+ .tool summary::-webkit-details-marker { display: none; }
174
+ .tool summary::before { content: ""; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--gl-border); flex: none; align-self: center; }
175
+ .tool.running summary::before { background: var(--mode); animation: pulse 1.2s ease-in-out infinite; }
176
+ .tool.done summary::before { background: var(--add); }
177
+ .tool.error summary::before { background: var(--rem); }
178
+ .tool .args { font-family: var(--gl-font-mono); font-size: 0.78rem; overflow-wrap: anywhere; }
179
+ .tool pre { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 0.78rem; max-height: 18rem; overflow-y: auto; background: var(--gl-surface); border: 1px solid var(--gl-border); border-radius: var(--gl-radius-md); padding: 0.6rem; color: var(--gl-text); }
180
+ .tool .open { margin-left: 0.25rem; padding: 0.1rem 0.5rem; border: 1px solid var(--gl-border); font-size: 0.78rem; }
181
+ .note { margin-top: 1rem; padding: 0.6rem 0.9rem; border-radius: var(--gl-radius-md); background: var(--gl-surface); border: 1px solid var(--gl-border); font-size: 0.9rem; white-space: pre-wrap; overflow-wrap: anywhere; }
182
+ .note.warning { border-left: 3px solid var(--warn); }
183
+ .note.error { border-left: 3px solid var(--rem); }
184
+ .note.mono { font-family: var(--gl-font-mono); font-size: 0.82rem; }
185
+ .working { margin-top: 0.75rem; color: var(--gl-text-muted); font-size: 0.88rem; display: flex; gap: 0.5rem; align-items: center; }
186
+ .working::before { content: ""; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--mode); animation: pulse 1.2s ease-in-out infinite; }
187
+ @keyframes pulse { 50% { opacity: 0.3; } }
188
+ @media (prefers-reduced-motion: reduce) { .working::before, .tool.running summary::before { animation: none; } }
189
+
190
+ .empty { text-align: center; padding-top: 12vh; }
191
+ .empty h1 { font-family: var(--gl-font-serif); font-weight: 600; font-size: 1.6rem; margin: 0 0 1rem; }
192
+ .chips { display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: center; }
193
+ .chips button { border: 1px solid var(--gl-border); border-radius: var(--gl-radius-pill); background: var(--gl-surface-raised); padding: 0.4rem 0.9rem; }
194
+ .chips button:hover { border-color: var(--mode); }
195
+
196
+ /* ---- composer */
197
+ .composer { position: relative; display: flex; gap: 0.6rem; align-items: flex-end; padding: 0.75rem 1rem 1rem; border-top: 1px solid var(--gl-border); background: var(--gl-bg); }
198
+ .composer textarea {
199
+ flex: 1; resize: none; max-height: 40vh; min-height: 2.8rem; padding: 0.6rem 0.8rem;
200
+ border: 1px solid var(--gl-border); border-radius: var(--gl-radius-lg); background: var(--gl-surface-raised);
201
+ }
202
+ .composer textarea:focus { outline: none; border-color: var(--mode); box-shadow: 0 0 0 1px var(--mode); }
203
+ .composer-buttons { display: flex; gap: 0.4rem; }
204
+ .suggest { position: absolute; left: 1rem; right: 1rem; bottom: calc(100% - 0.25rem); max-width: 30rem; max-height: 16rem; overflow-y: auto; background: var(--gl-surface-raised); border: 1px solid var(--gl-border); border-radius: var(--gl-radius-md); box-shadow: 0 8px 24px -12px rgba(0,0,0,0.35); padding: 0.25rem; }
205
+ .suggest button { display: block; width: 100%; text-align: left; padding: 0.35rem 0.6rem; }
206
+ .suggest button[aria-selected="true"], .suggest button:hover { background: var(--gl-bg); }
207
+ .suggest .name { font-family: var(--gl-font-mono); font-size: 0.85rem; }
208
+ .suggest .desc { color: var(--gl-text-muted); font-size: 0.8rem; margin-left: 0.5rem; }
209
+
210
+ /* ---- dialogs */
211
+ .modal { border: 1px solid var(--gl-border); border-radius: var(--gl-radius-lg); background: var(--gl-surface-raised); color: var(--gl-text); padding: 1.25rem 1.4rem; width: min(40rem, calc(100vw - 2rem)); max-height: calc(100dvh - 2rem); overflow-y: auto; box-shadow: 0 24px 60px -20px rgba(0,0,0,0.5); }
212
+ .modal.wide { width: min(46rem, calc(100vw - 2rem)); }
213
+ .modal::backdrop { background: rgba(16, 22, 28, 0.55); backdrop-filter: blur(2px); }
214
+ .modal h2 { font-family: var(--gl-font-serif); font-size: 1.25rem; margin: 0 0 0.25rem; }
215
+ .modal h3 { font-size: 0.95rem; margin: 1.25rem 0 0.5rem; }
216
+ .modal .hint { color: var(--gl-text-muted); margin: 0 0 0.75rem; font-size: 0.9rem; }
217
+ .preview { font-family: var(--gl-font-mono); font-size: 0.82rem; line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; background: var(--gl-surface); border: 1px solid var(--gl-border); border-radius: var(--gl-radius-md); padding: 0.75rem; max-height: 55vh; overflow-y: auto; margin: 0 0 1rem; }
218
+ .preview .add { color: var(--add); background: rgba(29, 122, 58, 0.12); padding: 0.1em 0.35em; border-radius: var(--gl-radius-sm); font-weight: 600; }
219
+ .preview .rem { color: var(--rem); background: rgba(179, 38, 30, 0.12); padding: 0.1em 0.35em; border-radius: var(--gl-radius-sm); font-weight: 600; }
220
+ .modal-buttons { display: flex; justify-content: flex-end; align-items: center; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem; }
221
+ .modal-shortcut-hint { font-size: 0.76rem; color: var(--gl-text-muted); font-family: var(--gl-font-mono); margin-right: auto; }
222
+ .modal-head { display: flex; justify-content: space-between; align-items: center; }
223
+ .modal input[type="text"], .modal input[type="password"], .modal textarea, .modal select { width: 100%; padding: 0.5rem 0.6rem; border: 1px solid var(--gl-border); border-radius: var(--gl-radius-md); background: var(--gl-bg); }
224
+ .modal textarea { min-height: 12rem; font-family: var(--gl-font-mono); font-size: 0.85rem; }
225
+ .login { display: grid; gap: 0.6rem; }
226
+ .login label { display: grid; gap: 0.25rem; font-size: 0.9rem; }
227
+ .login p { margin: 0; }
228
+ .check { display: flex; gap: 0.4rem; align-items: center; font-size: 0.88rem; margin-bottom: 0.5rem; }
229
+ .models { list-style: none; margin: 0; padding: 0; max-height: 16rem; overflow-y: auto; border: 1px solid var(--gl-border); border-radius: var(--gl-radius-md); }
230
+ .models li + li { border-top: 1px solid var(--gl-border); }
231
+ .models button { width: 100%; text-align: left; border-radius: 0; font-family: var(--gl-font-mono); font-size: 0.85rem; padding: 0.45rem 0.7rem; }
232
+ .models button:hover { background: var(--gl-bg); }
233
+ .models .current { font-weight: 700; color: var(--mode); }
234
+ .models .empty-models { padding: 0.6rem 0.7rem; color: var(--gl-text-muted); font-size: 0.9rem; }
235
+
236
+ .side .phone-only { display: none; }
237
+ .settings { display: grid; gap: 0.6rem; }
238
+ .settings label { display: grid; gap: 0.25rem; font-size: 0.85rem; color: var(--gl-text-muted); }
239
+ .settings select { border: 1px solid var(--gl-border); border-radius: var(--gl-radius-md); background: var(--gl-surface-raised); padding: 0.4rem 0.5rem; color: var(--gl-text); }
240
+ .settings .model-btn { text-align: left; color: var(--gl-text); }
241
+
242
+ /* ---- phone */
243
+ @media (max-width: 52rem) {
244
+ .app { grid-template-columns: minmax(0, 1fr); }
245
+ .menu-btn { display: inline-block; border: 1px solid var(--gl-border); }
246
+ .side { position: fixed; z-index: 20; top: 0; bottom: 0; left: 0; width: min(20rem, 85vw); transform: translateX(-100%); transition: transform 0.2s ease; box-shadow: 0 0 40px -10px rgba(0,0,0,0.5); }
247
+ .side.open { transform: none; }
248
+ .status { margin-left: 0; width: 100%; }
249
+ .status .profile, .status .model-btn { display: none; }
250
+ .side .phone-only { display: grid; }
251
+ .top { gap: 0.5rem 0.75rem; }
252
+ .log { padding: 1rem 0.75rem 1.5rem; }
253
+ .composer { padding: 0.6rem 0.75rem 0.75rem; }
254
+ .composer-buttons { flex-direction: column; }
255
+ }
256
+ @media (prefers-reduced-motion: reduce) { .side { transition: none; } }