@ammduncan/easel 0.2.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.
@@ -0,0 +1,441 @@
1
+ /* easel — index page (session list) */
2
+
3
+ .index-main {
4
+ width: min(92vw, 1100px);
5
+ margin: 0 auto;
6
+ padding: 56px 28px 160px;
7
+ }
8
+
9
+ @media (min-width: 1800px) {
10
+ .index-main { width: min(70vw, 1280px); }
11
+ }
12
+
13
+ .page-head {
14
+ margin-bottom: 36px;
15
+ }
16
+
17
+ .page-head h1 {
18
+ font-size: 38px;
19
+ font-weight: 500;
20
+ letter-spacing: -0.025em;
21
+ margin: 0 0 10px;
22
+ }
23
+
24
+ .page-deck {
25
+ font-size: 18px;
26
+ color: var(--ds-ink-soft);
27
+ margin: 0;
28
+ max-width: 620px;
29
+ }
30
+
31
+ .session-list {
32
+ list-style: none;
33
+ padding: 0;
34
+ margin: 0;
35
+ display: flex;
36
+ flex-direction: column;
37
+ gap: 12px;
38
+ }
39
+
40
+ .session-row {
41
+ position: relative;
42
+ display: grid;
43
+ grid-template-columns: 1fr auto;
44
+ gap: 18px;
45
+ align-items: start;
46
+ padding: 20px 22px;
47
+ background: var(--ds-surface);
48
+ border: 1px solid var(--ds-line);
49
+ border-radius: 14px;
50
+ box-shadow: var(--ds-shadow-sm);
51
+ cursor: pointer;
52
+ text-decoration: none;
53
+ color: inherit;
54
+ transition: border-color 150ms ease, background 150ms ease, transform 120ms ease;
55
+ }
56
+
57
+ .session-del {
58
+ position: absolute;
59
+ top: 14px;
60
+ right: 14px;
61
+ display: inline-flex;
62
+ align-items: center;
63
+ justify-content: center;
64
+ width: 22px;
65
+ height: 22px;
66
+ background: transparent;
67
+ border: 0;
68
+ border-radius: 6px;
69
+ color: var(--ds-muted);
70
+ cursor: pointer;
71
+ opacity: 0;
72
+ transition: opacity 120ms ease, background 120ms ease, color 120ms ease;
73
+ }
74
+ .session-row:hover .session-del { opacity: 0.6; }
75
+ .session-del:hover {
76
+ background: var(--ds-surface-soft);
77
+ color: var(--ds-danger);
78
+ opacity: 1 !important;
79
+ }
80
+
81
+ .switcher-item {
82
+ position: relative;
83
+ }
84
+ .switcher-del {
85
+ position: absolute;
86
+ top: 50%;
87
+ right: 36px;
88
+ transform: translateY(-50%);
89
+ display: inline-flex;
90
+ align-items: center;
91
+ justify-content: center;
92
+ width: 22px;
93
+ height: 22px;
94
+ background: transparent;
95
+ border: 0;
96
+ border-radius: 6px;
97
+ color: var(--ds-muted);
98
+ cursor: pointer;
99
+ opacity: 0;
100
+ transition: opacity 120ms ease, background 120ms ease, color 120ms ease;
101
+ }
102
+ .switcher-item:hover .switcher-del { opacity: 0.65; }
103
+ .switcher-del:hover {
104
+ background: var(--ds-surface-soft);
105
+ color: var(--ds-danger);
106
+ opacity: 1 !important;
107
+ }
108
+
109
+ .session-row:hover {
110
+ border-color: color-mix(in srgb, var(--ds-accent) 50%, var(--ds-line));
111
+ transform: translateY(-1px);
112
+ }
113
+
114
+ .session-row:active {
115
+ transform: translateY(0);
116
+ }
117
+
118
+ .session-head {
119
+ display: flex;
120
+ align-items: center;
121
+ gap: 10px;
122
+ margin-bottom: 8px;
123
+ }
124
+
125
+ .session-project {
126
+ font-size: 16px;
127
+ font-weight: 600;
128
+ color: var(--ds-ink);
129
+ letter-spacing: -0.005em;
130
+ }
131
+
132
+ .session-id {
133
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
134
+ font-size: 11.5px;
135
+ padding: 2px 8px;
136
+ border-radius: 999px;
137
+ background: var(--ds-surface-soft);
138
+ color: var(--ds-muted);
139
+ }
140
+
141
+ .session-unread {
142
+ width: 8px;
143
+ height: 8px;
144
+ border-radius: 50%;
145
+ background: var(--ds-accent);
146
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--ds-accent) 22%, transparent);
147
+ }
148
+
149
+ .session-last-push {
150
+ font-size: 14px;
151
+ color: var(--ds-ink-soft);
152
+ margin: 0;
153
+ overflow: hidden;
154
+ text-overflow: ellipsis;
155
+ white-space: nowrap;
156
+ }
157
+
158
+ .session-last-push .none {
159
+ color: var(--ds-muted);
160
+ font-style: italic;
161
+ }
162
+
163
+ .session-meta {
164
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
165
+ font-size: 11.5px;
166
+ color: var(--ds-muted);
167
+ margin-top: 8px;
168
+ }
169
+
170
+ .session-meta .sep {
171
+ margin: 0 7px;
172
+ opacity: 0.5;
173
+ }
174
+
175
+ .session-right {
176
+ display: flex;
177
+ flex-direction: column;
178
+ align-items: flex-end;
179
+ gap: 6px;
180
+ white-space: nowrap;
181
+ padding-right: 34px;
182
+ }
183
+
184
+ .session-pushcount {
185
+ font-size: 13px;
186
+ font-weight: 600;
187
+ color: var(--ds-ink);
188
+ }
189
+
190
+ .session-when {
191
+ font-size: 11.5px;
192
+ color: var(--ds-muted);
193
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
194
+ }
195
+
196
+ .index-empty {
197
+ text-align: center;
198
+ padding: 120px 24px;
199
+ color: var(--ds-muted);
200
+ }
201
+
202
+ .index-empty h2 {
203
+ font-size: 24px;
204
+ font-weight: 500;
205
+ color: var(--ds-ink);
206
+ margin: 0 0 8px;
207
+ }
208
+
209
+ .index-empty p {
210
+ font-size: 15px;
211
+ margin: 0;
212
+ }
213
+
214
+ /* Preset picker — three swatch buttons in the topbar */
215
+ .preset-group {
216
+ display: inline-flex;
217
+ align-items: center;
218
+ gap: 4px;
219
+ padding: 3px;
220
+ background: var(--ds-surface);
221
+ border: 1px solid var(--ds-line);
222
+ border-radius: 10px;
223
+ }
224
+ .preset-btn {
225
+ display: inline-flex;
226
+ align-items: center;
227
+ justify-content: center;
228
+ width: 26px;
229
+ height: 26px;
230
+ border: 0;
231
+ background: transparent;
232
+ border-radius: 7px;
233
+ cursor: pointer;
234
+ padding: 0;
235
+ transition: background 120ms ease, transform 120ms ease;
236
+ }
237
+ .preset-btn:hover { background: var(--ds-surface-soft); }
238
+ .preset-btn:active { transform: scale(0.95); }
239
+ .preset-btn.active { background: var(--ds-accent-soft); }
240
+ .preset-swatch {
241
+ width: 16px;
242
+ height: 16px;
243
+ border-radius: 50%;
244
+ display: block;
245
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15);
246
+ }
247
+ .swatch-paper { background: linear-gradient(135deg, #f4bf5e, #c97a1c); }
248
+ .swatch-aurora { background: linear-gradient(135deg, #b8c8ff, #6d4eff); }
249
+ .swatch-slate { background: linear-gradient(135deg, #7dd3fc, #2f5fd1); }
250
+
251
+ /* Density toggle — two-button group, mirrors .preset-group */
252
+ .density-group {
253
+ display: inline-flex;
254
+ align-items: center;
255
+ gap: 4px;
256
+ padding: 3px;
257
+ background: var(--ds-surface);
258
+ border: 1px solid var(--ds-line);
259
+ border-radius: 10px;
260
+ }
261
+ .density-btn {
262
+ display: inline-flex;
263
+ align-items: center;
264
+ justify-content: center;
265
+ width: 26px;
266
+ height: 26px;
267
+ border: 0;
268
+ background: transparent;
269
+ border-radius: 7px;
270
+ cursor: pointer;
271
+ padding: 0;
272
+ color: var(--ds-muted);
273
+ transition: background 120ms ease, color 120ms ease, transform 120ms ease;
274
+ }
275
+ .density-btn:hover { background: var(--ds-surface-soft); color: var(--ds-ink); }
276
+ .density-btn.active { background: var(--ds-accent-soft); color: var(--ds-accent); }
277
+ .density-btn:active { transform: scale(0.95); }
278
+ .density-btn svg { display: block; }
279
+
280
+ /* Back link in viewer */
281
+ .back-link {
282
+ display: inline-flex;
283
+ align-items: center;
284
+ gap: 6px;
285
+ font-size: 12.5px;
286
+ color: var(--ds-muted);
287
+ text-decoration: none;
288
+ padding: 5px 10px;
289
+ border-radius: 8px;
290
+ transition: background 120ms ease, color 120ms ease;
291
+ }
292
+
293
+ .back-link:hover {
294
+ background: var(--ds-surface-soft);
295
+ color: var(--ds-ink);
296
+ }
297
+
298
+ /* Session switcher (in viewer topbar) */
299
+ .switcher {
300
+ position: relative;
301
+ }
302
+
303
+ .switcher-btn {
304
+ display: inline-flex;
305
+ align-items: center;
306
+ gap: 6px;
307
+ height: 30px;
308
+ padding: 0 10px;
309
+ font-size: 12.5px;
310
+ background: var(--ds-surface);
311
+ border: 1px solid var(--ds-line);
312
+ color: var(--ds-ink-soft);
313
+ border-radius: 8px;
314
+ cursor: pointer;
315
+ font-family: inherit;
316
+ }
317
+
318
+ .switcher-btn:hover {
319
+ background: var(--ds-surface-soft);
320
+ color: var(--ds-ink);
321
+ }
322
+
323
+ .switcher-btn .chev {
324
+ font-size: 10px;
325
+ opacity: 0.7;
326
+ }
327
+
328
+ .switcher-menu {
329
+ position: absolute;
330
+ top: calc(100% + 6px);
331
+ right: 0;
332
+ min-width: 360px;
333
+ max-width: 460px;
334
+ max-height: min(70vh, 540px);
335
+ display: flex;
336
+ flex-direction: column;
337
+ background: var(--ds-bg-elev);
338
+ border: 1px solid var(--ds-line);
339
+ border-radius: 12px;
340
+ box-shadow: var(--ds-shadow-md);
341
+ padding: 0;
342
+ z-index: 30;
343
+ overflow: hidden;
344
+ }
345
+ .switcher-search {
346
+ flex: 0 0 auto;
347
+ padding: 8px 10px;
348
+ border-bottom: 1px solid var(--ds-line-soft);
349
+ }
350
+ .switcher-search input {
351
+ width: 100%;
352
+ height: 32px;
353
+ padding: 0 10px;
354
+ background: var(--ds-surface);
355
+ color: var(--ds-ink);
356
+ border: 1px solid var(--ds-line);
357
+ border-radius: 8px;
358
+ font: inherit;
359
+ font-size: 13px;
360
+ outline: none;
361
+ }
362
+ .switcher-search input:focus {
363
+ border-color: var(--ds-accent);
364
+ box-shadow: 0 0 0 3px var(--ds-accent-soft);
365
+ }
366
+ .switcher-list {
367
+ flex: 1 1 auto;
368
+ overflow-y: auto;
369
+ padding: 6px;
370
+ }
371
+ .switcher-footerbar {
372
+ flex: 0 0 auto;
373
+ border-top: 1px solid var(--ds-line-soft);
374
+ padding: 4px;
375
+ }
376
+
377
+ .switcher-menu[hidden] {
378
+ display: none !important;
379
+ }
380
+
381
+ .switcher-item {
382
+ display: flex;
383
+ align-items: center;
384
+ gap: 10px;
385
+ padding: 9px 12px;
386
+ border-radius: 8px;
387
+ text-decoration: none;
388
+ color: var(--ds-ink);
389
+ font-size: 13px;
390
+ cursor: pointer;
391
+ }
392
+
393
+ .switcher-item:hover {
394
+ background: var(--ds-surface-soft);
395
+ }
396
+
397
+ .switcher-item.current {
398
+ background: var(--ds-accent-soft);
399
+ color: var(--ds-accent);
400
+ }
401
+
402
+ .switcher-item .project {
403
+ flex: 1;
404
+ overflow: hidden;
405
+ text-overflow: ellipsis;
406
+ white-space: nowrap;
407
+ font-weight: 500;
408
+ }
409
+
410
+ .switcher-item .count {
411
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
412
+ font-size: 11px;
413
+ color: var(--ds-muted);
414
+ }
415
+
416
+ .switcher-item .unread-dot {
417
+ width: 6px;
418
+ height: 6px;
419
+ border-radius: 50%;
420
+ background: var(--ds-accent);
421
+ }
422
+
423
+ .switcher-divider {
424
+ height: 1px;
425
+ background: var(--ds-line);
426
+ margin: 6px 4px;
427
+ }
428
+
429
+ .switcher-footer {
430
+ display: block;
431
+ padding: 9px 12px;
432
+ font-size: 12px;
433
+ color: var(--ds-muted);
434
+ text-decoration: none;
435
+ border-radius: 8px;
436
+ }
437
+
438
+ .switcher-footer:hover {
439
+ background: var(--ds-surface-soft);
440
+ color: var(--ds-ink);
441
+ }
@@ -0,0 +1,74 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
6
+ <title>easel · sessions</title>
7
+ <link rel="preconnect" href="https://rsms.me/" />
8
+ <link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
9
+ <link rel="stylesheet" href="/static/viewer.css" />
10
+ <link rel="stylesheet" href="/static/index.css" />
11
+ <script>
12
+ (function () {
13
+ try {
14
+ // One-time migration from the project's prior name.
15
+ if (!localStorage.getItem("easel:config")) {
16
+ var legacy = localStorage.getItem("claude-display:config");
17
+ if (legacy) localStorage.setItem("easel:config", legacy);
18
+ }
19
+ var stored = JSON.parse(localStorage.getItem("easel:config") || "null");
20
+ var preset = stored && stored.preset ? stored.preset : "paper";
21
+ var theme = stored && stored.theme ? stored.theme : "dark";
22
+ var density = stored && stored.density ? stored.density : "carded";
23
+ document.documentElement.setAttribute("data-preset", preset);
24
+ document.documentElement.setAttribute("data-theme", theme);
25
+ document.documentElement.setAttribute("data-density", density);
26
+ } catch (e) {
27
+ document.documentElement.setAttribute("data-preset", "paper");
28
+ document.documentElement.setAttribute("data-theme", "dark");
29
+ document.documentElement.setAttribute("data-density", "carded");
30
+ }
31
+ })();
32
+ </script>
33
+ </head>
34
+ <body>
35
+ <header class="topbar">
36
+ <div class="topbar-left">
37
+ <div class="brand">
38
+ <span class="brand-mark" aria-hidden="true"></span>
39
+ <span>easel</span>
40
+ </div>
41
+ <div class="count" id="session-count">— sessions</div>
42
+ </div>
43
+ <div class="topbar-right">
44
+ <div class="preset-group" role="group" aria-label="Theme preset">
45
+ <button class="preset-btn" data-preset="paper" type="button" aria-label="Paper preset" title="Paper · warm cream"><span class="preset-swatch swatch-paper"></span></button>
46
+ <button class="preset-btn" data-preset="aurora" type="button" aria-label="Aurora preset" title="Aurora · violet glow"><span class="preset-swatch swatch-aurora"></span></button>
47
+ <button class="preset-btn" data-preset="slate" type="button" aria-label="Slate preset" title="Slate · cool neutral"><span class="preset-swatch swatch-slate"></span></button>
48
+ </div>
49
+ <div class="density-group" role="group" aria-label="Density">
50
+ <button class="density-btn" data-density="carded" type="button" aria-label="Carded" title="Carded · bordered cards"><svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="2" y="3.5" width="12" height="3.5" rx="1"/><rect x="2" y="9" width="12" height="3.5" rx="1"/></svg></button>
51
+ <button class="density-btn" data-density="flat" type="button" aria-label="Flat" title="Flat · no chrome, whitespace only"><svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M3 4.5h10M3 11.5h10"/></svg></button>
52
+ </div>
53
+ <button id="theme-toggle" class="theme-toggle" type="button" aria-label="Toggle theme" title="Toggle light / dark">
54
+ <svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>
55
+ <svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
56
+ </button>
57
+ </div>
58
+ </header>
59
+
60
+ <main class="index-main">
61
+ <div class="page-head">
62
+ <h1>Sessions</h1>
63
+ <p class="page-deck">Every active Claude session that's registered with easel. Click any row to enter its feed.</p>
64
+ </div>
65
+ <ul id="session-list" class="session-list" aria-live="polite"></ul>
66
+ <div id="empty-state" class="index-empty" hidden>
67
+ <h2>No sessions yet</h2>
68
+ <p>Start a Claude Code chat and a session will appear here.</p>
69
+ </div>
70
+ </main>
71
+
72
+ <script src="/static/index.js"></script>
73
+ </body>
74
+ </html>