@tiagojct/subsub 0.5.0 → 0.6.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/README.md +16 -2
- package/assets/icon.icns +0 -0
- package/assets/icon.ico +0 -0
- package/assets/icon.png +0 -0
- package/dist/cli.js +12 -0
- package/dist/init.js +2 -2
- package/dist/shortcut.js +165 -0
- package/dist/subsub.js +40 -0
- package/dist/web.js +702 -0
- package/docs/Subsub.md +11 -0
- package/package.json +5 -3
- package/src/cli.ts +12 -0
- package/src/init.ts +2 -2
- package/src/shortcut.ts +184 -0
- package/src/subsub.ts +38 -0
- package/src/web.ts +710 -0
- package/web/app.css +203 -0
- package/web/app.js +859 -0
- package/web/fonts/IBMPlexMono.woff2 +0 -0
- package/web/fonts/IBMPlexSans.woff2 +0 -0
- package/web/fonts/IBMPlexSerif.woff2 +0 -0
- package/web/glauca.css +82 -0
- package/web/i18n.js +263 -0
- package/web/icon.svg +1 -0
- package/web/index.html +112 -0
- package/web/vendor/marked.esm.js +78 -0
package/web/app.css
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
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 {
|
|
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
|
+
body[data-mode="librarian"] { --mode: #a8520f; --mode-deep: #7f3d09; --on-mode: #fff8f0; }
|
|
35
|
+
@media (prefers-color-scheme: dark) {
|
|
36
|
+
body[data-mode="librarian"] { --mode: #e0832a; --mode-deep: #f09a48; --on-mode: #140c04; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
40
|
+
html, body { height: 100%; }
|
|
41
|
+
body {
|
|
42
|
+
margin: 0; background: var(--gl-bg); color: var(--gl-text);
|
|
43
|
+
font-family: var(--gl-font-sans); font-size: 1rem; line-height: 1.55;
|
|
44
|
+
}
|
|
45
|
+
button, input, select, textarea { font: inherit; color: inherit; }
|
|
46
|
+
a { color: var(--gl-accent); }
|
|
47
|
+
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
48
|
+
[hidden] { display: none !important; }
|
|
49
|
+
:focus-visible { outline: 2px solid var(--mode); outline-offset: 2px; }
|
|
50
|
+
|
|
51
|
+
/* ---- buttons */
|
|
52
|
+
button { cursor: pointer; border-radius: var(--gl-radius-md); border: 1px solid transparent; padding: 0.45rem 0.9rem; background: none; }
|
|
53
|
+
button:disabled { opacity: 0.5; cursor: default; }
|
|
54
|
+
.primary { background: var(--mode); color: var(--on-mode); font-weight: 600; }
|
|
55
|
+
.primary:hover:not(:disabled) { background: var(--mode-deep); }
|
|
56
|
+
.secondary { border-color: var(--gl-border); background: var(--gl-surface-raised); }
|
|
57
|
+
.secondary:hover:not(:disabled) { border-color: var(--mode); }
|
|
58
|
+
.ghost { color: var(--gl-text-muted); }
|
|
59
|
+
.ghost:hover { color: var(--gl-text); background: var(--gl-surface); }
|
|
60
|
+
.small { font-size: 0.85rem; }
|
|
61
|
+
.muted { color: var(--gl-text-muted); }
|
|
62
|
+
.block { width: 100%; }
|
|
63
|
+
|
|
64
|
+
/* ---- layout */
|
|
65
|
+
.app { display: grid; grid-template-columns: var(--side-w) minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); height: 100vh; height: 100dvh; }
|
|
66
|
+
.top {
|
|
67
|
+
grid-column: 1 / -1; display: flex; align-items: center; gap: 0.75rem 1.25rem; flex-wrap: wrap;
|
|
68
|
+
padding: 0.6rem 1rem; border-bottom: 1px solid var(--gl-border); background: var(--gl-surface);
|
|
69
|
+
}
|
|
70
|
+
.brand { font-family: var(--gl-font-serif); font-weight: 600; font-size: 1.15rem; display: flex; align-items: center; gap: 0.45rem; }
|
|
71
|
+
.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); }
|
|
72
|
+
.menu-btn { display: none; }
|
|
73
|
+
.modes { display: inline-flex; border: 1px solid var(--gl-border); border-radius: var(--gl-radius-pill); padding: 2px; background: var(--gl-bg); }
|
|
74
|
+
.modes button { border-radius: var(--gl-radius-pill); padding: 0.3rem 0.9rem; color: var(--gl-text-muted); }
|
|
75
|
+
.modes button[aria-pressed="true"] { background: var(--mode); color: var(--on-mode); font-weight: 600; }
|
|
76
|
+
.status { margin-left: auto; display: flex; align-items: center; gap: 0.5rem 0.9rem; flex-wrap: wrap; font-size: 0.88rem; }
|
|
77
|
+
.library { color: var(--gl-text-muted); }
|
|
78
|
+
.library.down { color: var(--rem); font-weight: 600; }
|
|
79
|
+
.status select { border: 1px solid var(--gl-border); border-radius: var(--gl-radius-md); background: var(--gl-surface-raised); padding: 0.3rem 0.5rem; }
|
|
80
|
+
.model-btn { font-family: var(--gl-font-mono); font-size: 0.8rem; border: 1px solid var(--gl-border); }
|
|
81
|
+
|
|
82
|
+
.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; }
|
|
83
|
+
.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; }
|
|
84
|
+
.side .grow { flex: 1; }
|
|
85
|
+
.actions { display: flex; flex-direction: column; gap: 0.25rem; }
|
|
86
|
+
.actions button { text-align: left; padding: 0.35rem 0.6rem; border-radius: var(--gl-radius-md); }
|
|
87
|
+
.actions button:hover { background: var(--gl-bg); }
|
|
88
|
+
.actions hr { border: 0; border-top: 1px solid var(--gl-border); margin: 0.35rem 0; width: 100%; }
|
|
89
|
+
.sessions { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.15rem; }
|
|
90
|
+
.sessions button { width: 100%; text-align: left; padding: 0.4rem 0.6rem; display: block; }
|
|
91
|
+
.sessions button:hover { background: var(--gl-bg); }
|
|
92
|
+
.sessions .current button { background: var(--gl-bg); border-color: var(--gl-border); }
|
|
93
|
+
.sessions .when { display: block; font-size: 0.75rem; color: var(--gl-text-muted); }
|
|
94
|
+
.sessions .what { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; font-size: 0.88rem; }
|
|
95
|
+
.side-foot { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; }
|
|
96
|
+
.version { font-size: 0.75rem; color: var(--gl-text-muted); font-family: var(--gl-font-mono); }
|
|
97
|
+
|
|
98
|
+
/* ---- conversation */
|
|
99
|
+
.chat { display: flex; flex-direction: column; min-height: 0; }
|
|
100
|
+
.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; }
|
|
101
|
+
.log { flex: 1; overflow-y: auto; padding: 1.25rem 1rem 2rem; }
|
|
102
|
+
.log > * { max-width: 48rem; margin-left: auto; margin-right: auto; }
|
|
103
|
+
.msg { margin-top: 1.1rem; }
|
|
104
|
+
.msg.user { display: flex; justify-content: flex-end; }
|
|
105
|
+
.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; }
|
|
106
|
+
.msg.assistant .md { overflow-wrap: anywhere; }
|
|
107
|
+
.md > :first-child { margin-top: 0; }
|
|
108
|
+
.md > :last-child { margin-bottom: 0; }
|
|
109
|
+
.md p, .md ul, .md ol { margin: 0.6rem 0; }
|
|
110
|
+
.md h1, .md h2, .md h3 { font-family: var(--gl-font-serif); line-height: 1.25; margin: 1.1rem 0 0.4rem; }
|
|
111
|
+
.md h1 { font-size: 1.35rem; } .md h2 { font-size: 1.2rem; } .md h3 { font-size: 1.05rem; }
|
|
112
|
+
.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; }
|
|
113
|
+
.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; }
|
|
114
|
+
.md pre code { background: none; border: 0; padding: 0; }
|
|
115
|
+
.md blockquote { margin: 0.6rem 0; padding-left: 0.9rem; border-left: 3px solid var(--gl-tint-pale); color: var(--gl-text-muted); }
|
|
116
|
+
.md table { border-collapse: collapse; display: block; overflow-x: auto; font-size: 0.9rem; }
|
|
117
|
+
.md th, .md td { border-bottom: 1px solid var(--gl-border); padding: 0.3rem 0.6rem; text-align: left; vertical-align: top; }
|
|
118
|
+
.md th { font-size: 0.82rem; color: var(--gl-text-muted); }
|
|
119
|
+
.tool { margin-top: 0.5rem; font-size: 0.86rem; color: var(--gl-text-muted); }
|
|
120
|
+
.tool summary { cursor: pointer; list-style: none; display: flex; gap: 0.5rem; align-items: baseline; flex-wrap: wrap; }
|
|
121
|
+
.tool summary::-webkit-details-marker { display: none; }
|
|
122
|
+
.tool summary::before { content: ""; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--gl-border); flex: none; align-self: center; }
|
|
123
|
+
.tool.running summary::before { background: var(--mode); animation: pulse 1.2s ease-in-out infinite; }
|
|
124
|
+
.tool.done summary::before { background: var(--add); }
|
|
125
|
+
.tool.error summary::before { background: var(--rem); }
|
|
126
|
+
.tool .args { font-family: var(--gl-font-mono); font-size: 0.78rem; overflow-wrap: anywhere; }
|
|
127
|
+
.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); }
|
|
128
|
+
.tool .open { margin-left: 0.25rem; padding: 0.1rem 0.5rem; border: 1px solid var(--gl-border); font-size: 0.78rem; }
|
|
129
|
+
.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; }
|
|
130
|
+
.note.warning { border-left: 3px solid var(--warn); }
|
|
131
|
+
.note.error { border-left: 3px solid var(--rem); }
|
|
132
|
+
.note.mono { font-family: var(--gl-font-mono); font-size: 0.82rem; }
|
|
133
|
+
.working { margin-top: 0.75rem; color: var(--gl-text-muted); font-size: 0.88rem; display: flex; gap: 0.5rem; align-items: center; }
|
|
134
|
+
.working::before { content: ""; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--mode); animation: pulse 1.2s ease-in-out infinite; }
|
|
135
|
+
@keyframes pulse { 50% { opacity: 0.3; } }
|
|
136
|
+
@media (prefers-reduced-motion: reduce) { .working::before, .tool.running summary::before { animation: none; } }
|
|
137
|
+
|
|
138
|
+
.empty { text-align: center; padding-top: 12vh; }
|
|
139
|
+
.empty h1 { font-family: var(--gl-font-serif); font-weight: 600; font-size: 1.6rem; margin: 0 0 1rem; }
|
|
140
|
+
.chips { display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: center; }
|
|
141
|
+
.chips button { border: 1px solid var(--gl-border); border-radius: var(--gl-radius-pill); background: var(--gl-surface-raised); padding: 0.4rem 0.9rem; }
|
|
142
|
+
.chips button:hover { border-color: var(--mode); }
|
|
143
|
+
|
|
144
|
+
/* ---- composer */
|
|
145
|
+
.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); }
|
|
146
|
+
.composer textarea {
|
|
147
|
+
flex: 1; resize: none; max-height: 40vh; min-height: 2.8rem; padding: 0.6rem 0.8rem;
|
|
148
|
+
border: 1px solid var(--gl-border); border-radius: var(--gl-radius-lg); background: var(--gl-surface-raised);
|
|
149
|
+
}
|
|
150
|
+
.composer textarea:focus { outline: none; border-color: var(--mode); box-shadow: 0 0 0 1px var(--mode); }
|
|
151
|
+
.composer-buttons { display: flex; gap: 0.4rem; }
|
|
152
|
+
.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; }
|
|
153
|
+
.suggest button { display: block; width: 100%; text-align: left; padding: 0.35rem 0.6rem; }
|
|
154
|
+
.suggest button[aria-selected="true"], .suggest button:hover { background: var(--gl-bg); }
|
|
155
|
+
.suggest .name { font-family: var(--gl-font-mono); font-size: 0.85rem; }
|
|
156
|
+
.suggest .desc { color: var(--gl-text-muted); font-size: 0.8rem; margin-left: 0.5rem; }
|
|
157
|
+
|
|
158
|
+
/* ---- dialogs */
|
|
159
|
+
.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); }
|
|
160
|
+
.modal.wide { width: min(46rem, calc(100vw - 2rem)); }
|
|
161
|
+
.modal::backdrop { background: rgba(16, 22, 28, 0.55); }
|
|
162
|
+
.modal h2 { font-family: var(--gl-font-serif); font-size: 1.25rem; margin: 0 0 0.25rem; }
|
|
163
|
+
.modal h3 { font-size: 0.95rem; margin: 1.25rem 0 0.5rem; }
|
|
164
|
+
.modal .hint { color: var(--gl-text-muted); margin: 0 0 0.75rem; font-size: 0.9rem; }
|
|
165
|
+
.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; }
|
|
166
|
+
.preview .add { color: var(--add); font-weight: 600; }
|
|
167
|
+
.preview .rem { color: var(--rem); font-weight: 600; }
|
|
168
|
+
.modal-buttons { display: flex; justify-content: flex-end; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem; }
|
|
169
|
+
.modal-head { display: flex; justify-content: space-between; align-items: center; }
|
|
170
|
+
.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); }
|
|
171
|
+
.modal textarea { min-height: 12rem; font-family: var(--gl-font-mono); font-size: 0.85rem; }
|
|
172
|
+
.login { display: grid; gap: 0.6rem; }
|
|
173
|
+
.login label { display: grid; gap: 0.25rem; font-size: 0.9rem; }
|
|
174
|
+
.login p { margin: 0; }
|
|
175
|
+
.check { display: flex; gap: 0.4rem; align-items: center; font-size: 0.88rem; margin-bottom: 0.5rem; }
|
|
176
|
+
.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); }
|
|
177
|
+
.models li + li { border-top: 1px solid var(--gl-border); }
|
|
178
|
+
.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; }
|
|
179
|
+
.models button:hover { background: var(--gl-bg); }
|
|
180
|
+
.models .current { font-weight: 700; color: var(--mode); }
|
|
181
|
+
.models .empty-models { padding: 0.6rem 0.7rem; color: var(--gl-text-muted); font-size: 0.9rem; }
|
|
182
|
+
|
|
183
|
+
.side .phone-only { display: none; }
|
|
184
|
+
.settings { display: grid; gap: 0.6rem; }
|
|
185
|
+
.settings label { display: grid; gap: 0.25rem; font-size: 0.85rem; color: var(--gl-text-muted); }
|
|
186
|
+
.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); }
|
|
187
|
+
.settings .model-btn { text-align: left; color: var(--gl-text); }
|
|
188
|
+
|
|
189
|
+
/* ---- phone */
|
|
190
|
+
@media (max-width: 52rem) {
|
|
191
|
+
.app { grid-template-columns: minmax(0, 1fr); }
|
|
192
|
+
.menu-btn { display: inline-block; border: 1px solid var(--gl-border); }
|
|
193
|
+
.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); }
|
|
194
|
+
.side.open { transform: none; }
|
|
195
|
+
.status { margin-left: 0; width: 100%; }
|
|
196
|
+
.status .profile, .status .model-btn { display: none; }
|
|
197
|
+
.side .phone-only { display: grid; }
|
|
198
|
+
.top { gap: 0.5rem 0.75rem; }
|
|
199
|
+
.log { padding: 1rem 0.75rem 1.5rem; }
|
|
200
|
+
.composer { padding: 0.6rem 0.75rem 0.75rem; }
|
|
201
|
+
.composer-buttons { flex-direction: column; }
|
|
202
|
+
}
|
|
203
|
+
@media (prefers-reduced-motion: reduce) { .side { transition: none; } }
|