driggsby 0.2.1 → 0.2.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/dist/init/templates.js +134 -19
- package/package.json +3 -3
package/dist/init/templates.js
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
// (`driggsby dev` locally, or the Driggsby console after a deploy). The
|
|
5
5
|
// sample values below are invented for the template and match the real
|
|
6
6
|
// tool result shapes only in structure, so the scaffold's render functions
|
|
7
|
-
// work unchanged when live data replaces them.
|
|
7
|
+
// work unchanged when live data replaces them. The HTML ships a skeleton
|
|
8
|
+
// of the final layout so the first paint — in every context, before any
|
|
9
|
+
// script runs — is muted bars that the first render replaces, fading in
|
|
10
|
+
// at the exact same size so nothing shifts.
|
|
8
11
|
export function indexHtml(slug) {
|
|
9
12
|
return `<!doctype html>
|
|
10
13
|
<html lang="en">
|
|
@@ -21,12 +24,56 @@ export function indexHtml(slug) {
|
|
|
21
24
|
<p class="sample-note" id="sample-note" hidden>Sample data — your real
|
|
22
25
|
accounts appear when this app runs inside Driggsby.</p>
|
|
23
26
|
</header>
|
|
27
|
+
<!-- The bars below are skeleton placeholders. app.js replaces them
|
|
28
|
+
with data on its first render; edit the render functions there,
|
|
29
|
+
and keep the four stat bars in step with what renderOverview
|
|
30
|
+
draws. (The account rows are just a plausible list length —
|
|
31
|
+
the real count comes from the data.) -->
|
|
24
32
|
<section aria-label="Overview">
|
|
25
|
-
<div class="stat-grid" id="overview"
|
|
33
|
+
<div class="stat-grid" id="overview" aria-busy="true">
|
|
34
|
+
<div class="stat">
|
|
35
|
+
<div class="skeleton skeleton-label"></div>
|
|
36
|
+
<div class="skeleton skeleton-value"></div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="stat">
|
|
39
|
+
<div class="skeleton skeleton-label"></div>
|
|
40
|
+
<div class="skeleton skeleton-value"></div>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="stat">
|
|
43
|
+
<div class="skeleton skeleton-label"></div>
|
|
44
|
+
<div class="skeleton skeleton-value"></div>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="stat">
|
|
47
|
+
<div class="skeleton skeleton-label"></div>
|
|
48
|
+
<div class="skeleton skeleton-value"></div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
26
51
|
</section>
|
|
27
52
|
<section aria-label="Accounts">
|
|
28
53
|
<h2>Accounts</h2>
|
|
29
|
-
<div id="accounts"
|
|
54
|
+
<div id="accounts" aria-busy="true">
|
|
55
|
+
<div class="account-row">
|
|
56
|
+
<div class="account-names">
|
|
57
|
+
<div class="skeleton skeleton-name"></div>
|
|
58
|
+
<div class="skeleton skeleton-institution"></div>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="skeleton skeleton-balance"></div>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="account-row">
|
|
63
|
+
<div class="account-names">
|
|
64
|
+
<div class="skeleton skeleton-name"></div>
|
|
65
|
+
<div class="skeleton skeleton-institution"></div>
|
|
66
|
+
</div>
|
|
67
|
+
<div class="skeleton skeleton-balance"></div>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="account-row">
|
|
70
|
+
<div class="account-names">
|
|
71
|
+
<div class="skeleton skeleton-name"></div>
|
|
72
|
+
<div class="skeleton skeleton-institution"></div>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="skeleton skeleton-balance"></div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
30
77
|
</section>
|
|
31
78
|
</main>
|
|
32
79
|
<script type="module" src="/-/driggsby-sdk.js"></script>
|
|
@@ -104,6 +151,19 @@ function element(tag, className, text) {
|
|
|
104
151
|
return node;
|
|
105
152
|
}
|
|
106
153
|
|
|
154
|
+
// The page ships with a skeleton of this exact layout in its HTML — muted
|
|
155
|
+
// bars where the numbers will be — so the very first paint is already the
|
|
156
|
+
// final shape. The first render into a container replaces its skeleton and
|
|
157
|
+
// fades in, so data arriving reads as the page settling, never as content
|
|
158
|
+
// popping. classList.add is idempotent and re-adding a class never restarts
|
|
159
|
+
// its animation, so later live updates repaint in place without re-fading.
|
|
160
|
+
// aria-busy ships in the HTML so assistive tech hears "loading" until the
|
|
161
|
+
// first real content lands.
|
|
162
|
+
function revealOnce(container) {
|
|
163
|
+
container.classList.add("fade-in");
|
|
164
|
+
container.removeAttribute("aria-busy");
|
|
165
|
+
}
|
|
166
|
+
|
|
107
167
|
function renderOverview(result) {
|
|
108
168
|
const rollups = (result && result.summary_rollups) || {};
|
|
109
169
|
const container = document.getElementById("overview");
|
|
@@ -122,6 +182,7 @@ function renderOverview(result) {
|
|
|
122
182
|
);
|
|
123
183
|
container.append(stat);
|
|
124
184
|
}
|
|
185
|
+
revealOnce(container);
|
|
125
186
|
}
|
|
126
187
|
|
|
127
188
|
function renderAccounts(result) {
|
|
@@ -143,32 +204,32 @@ function renderAccounts(result) {
|
|
|
143
204
|
row.append(names, element("div", "account-balance", formatMoney(account.current_balance)));
|
|
144
205
|
container.append(row);
|
|
145
206
|
}
|
|
207
|
+
revealOnce(container);
|
|
146
208
|
}
|
|
147
209
|
|
|
148
210
|
// ---------------------------------------------------------------------------
|
|
149
211
|
// Live data. window.driggsby exists when the Driggsby SDK loaded — inside
|
|
150
212
|
// Driggsby, or under driggsby dev. Whether anything embeds this page is
|
|
151
213
|
// knowable synchronously: opened directly in a tab, no host will ever
|
|
152
|
-
// answer, so the sample data
|
|
153
|
-
// is revealed (it ships hidden in the HTML, so an embedded
|
|
154
|
-
// shows it, not even for the moment before this script runs).
|
|
155
|
-
// sample numbers never paint at all —
|
|
156
|
-
//
|
|
214
|
+
// answer, so the sample data replaces the skeleton right away and the note
|
|
215
|
+
// explaining it is revealed (it ships hidden in the HTML, so an embedded
|
|
216
|
+
// page never shows it, not even for the moment before this script runs).
|
|
217
|
+
// Embedded, sample numbers never paint at all — the shipped skeleton holds
|
|
218
|
+
// the layout until the first real results replace it and fade in.
|
|
157
219
|
// ---------------------------------------------------------------------------
|
|
158
220
|
|
|
159
221
|
const standalone = window.parent === window;
|
|
160
222
|
const note = document.getElementById("sample-note");
|
|
161
223
|
|
|
162
224
|
if (standalone) {
|
|
225
|
+
// The note and the sample values paint together (this whole branch is
|
|
226
|
+
// one synchronous task). The reveal is kept first so an edit that ever
|
|
227
|
+
// makes rendering async can't paint values before their label.
|
|
228
|
+
if (note) note.hidden = false;
|
|
163
229
|
renderOverview(SAMPLE_OVERVIEW);
|
|
164
230
|
renderAccounts(SAMPLE_ACCOUNTS);
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (note) note.remove();
|
|
168
|
-
const accounts = document.getElementById("accounts");
|
|
169
|
-
if (accounts) {
|
|
170
|
-
accounts.replaceChildren(element("div", "loading-note", "Loading your accounts…"));
|
|
171
|
-
}
|
|
231
|
+
} else if (note) {
|
|
232
|
+
note.remove();
|
|
172
233
|
}
|
|
173
234
|
|
|
174
235
|
if (window.driggsby) {
|
|
@@ -235,10 +296,64 @@ h2 {
|
|
|
235
296
|
display: none;
|
|
236
297
|
}
|
|
237
298
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
299
|
+
/* The skeleton ships in the HTML so the first paint is the final layout as
|
|
300
|
+
muted bars; the first render replaces it and fades in (see app.js). The
|
|
301
|
+
bar margins make each skeleton block exactly the height of the text it
|
|
302
|
+
stands in for, so nothing shifts when data lands. */
|
|
303
|
+
.skeleton {
|
|
304
|
+
border-radius: 4px;
|
|
305
|
+
background: var(--hairline);
|
|
306
|
+
animation: skeleton-pulse 1.6s ease-in-out infinite;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.skeleton-label {
|
|
310
|
+
width: 64px;
|
|
311
|
+
height: 12px;
|
|
312
|
+
margin: 3.75px 0 0;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.skeleton-value {
|
|
316
|
+
width: 96px;
|
|
317
|
+
height: 20px;
|
|
318
|
+
margin: 8.75px 0 5px;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.skeleton-name {
|
|
322
|
+
width: 150px;
|
|
323
|
+
height: 14px;
|
|
324
|
+
margin: 4px 0 8px;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.skeleton-institution {
|
|
328
|
+
width: 110px;
|
|
329
|
+
height: 12px;
|
|
330
|
+
margin: 4px 0;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.skeleton-balance {
|
|
334
|
+
width: 72px;
|
|
335
|
+
height: 14px;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
@keyframes skeleton-pulse {
|
|
339
|
+
0%, 100% { opacity: 1; }
|
|
340
|
+
50% { opacity: 0.55; }
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.fade-in {
|
|
344
|
+
animation: fade-in 200ms ease-out;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
@keyframes fade-in {
|
|
348
|
+
from { opacity: 0; }
|
|
349
|
+
to { opacity: 1; }
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
@media (prefers-reduced-motion: reduce) {
|
|
353
|
+
.skeleton,
|
|
354
|
+
.fade-in {
|
|
355
|
+
animation: none;
|
|
356
|
+
}
|
|
242
357
|
}
|
|
243
358
|
|
|
244
359
|
.stat-grid {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "driggsby",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Set up and build on Driggsby, the MCP server for personal finance.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"node": ">=18"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@driggsby/deploy": "0.2.
|
|
32
|
-
"@driggsby/sdk": "0.2.
|
|
31
|
+
"@driggsby/deploy": "0.2.2",
|
|
32
|
+
"@driggsby/sdk": "0.2.2"
|
|
33
33
|
}
|
|
34
34
|
}
|