driggsby 0.2.0 → 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 +152 -22
- 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">
|
|
@@ -18,15 +21,59 @@ export function indexHtml(slug) {
|
|
|
18
21
|
<main>
|
|
19
22
|
<header>
|
|
20
23
|
<h1>${slug}</h1>
|
|
21
|
-
<p class="sample-note" id="sample-note">Sample data — your real
|
|
22
|
-
appear when this app runs inside Driggsby.</p>
|
|
24
|
+
<p class="sample-note" id="sample-note" hidden>Sample data — your real
|
|
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>
|
|
@@ -46,8 +93,8 @@ export const APP_JS = `// Your app's code. Edit anything — driggsby dev reload
|
|
|
46
93
|
// real result shapes, so the render functions work unchanged either way.
|
|
47
94
|
|
|
48
95
|
// ---------------------------------------------------------------------------
|
|
49
|
-
// Sample data. Every name and number here is invented. It
|
|
50
|
-
//
|
|
96
|
+
// Sample data. Every name and number here is invented. It paints only
|
|
97
|
+
// when this page is opened on its own, outside Driggsby.
|
|
51
98
|
// ---------------------------------------------------------------------------
|
|
52
99
|
|
|
53
100
|
const SAMPLE_OVERVIEW = {
|
|
@@ -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,35 +204,39 @@ 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
|
-
// Driggsby, or under driggsby dev.
|
|
151
|
-
//
|
|
212
|
+
// Driggsby, or under driggsby dev. Whether anything embeds this page is
|
|
213
|
+
// knowable synchronously: opened directly in a tab, no host will ever
|
|
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.
|
|
152
219
|
// ---------------------------------------------------------------------------
|
|
153
220
|
|
|
154
|
-
|
|
155
|
-
|
|
221
|
+
const standalone = window.parent === window;
|
|
222
|
+
const note = document.getElementById("sample-note");
|
|
156
223
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (
|
|
162
|
-
|
|
163
|
-
|
|
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;
|
|
229
|
+
renderOverview(SAMPLE_OVERVIEW);
|
|
230
|
+
renderAccounts(SAMPLE_ACCOUNTS);
|
|
231
|
+
} else if (note) {
|
|
232
|
+
note.remove();
|
|
164
233
|
}
|
|
165
234
|
|
|
166
235
|
if (window.driggsby) {
|
|
167
236
|
driggsby.watch("get_overview", {}, (result) => {
|
|
168
|
-
overviewIsLive = true;
|
|
169
|
-
removeSampleNoteWhenAllLive();
|
|
170
237
|
renderOverview(result);
|
|
171
238
|
});
|
|
172
239
|
driggsby.watch("list_accounts", {}, (result) => {
|
|
173
|
-
accountsAreLive = true;
|
|
174
|
-
removeSampleNoteWhenAllLive();
|
|
175
240
|
renderAccounts(result);
|
|
176
241
|
});
|
|
177
242
|
}
|
|
@@ -226,6 +291,71 @@ h2 {
|
|
|
226
291
|
font-size: 13px;
|
|
227
292
|
}
|
|
228
293
|
|
|
294
|
+
/* The display rule above would defeat the hidden attribute without this. */
|
|
295
|
+
.sample-note[hidden] {
|
|
296
|
+
display: none;
|
|
297
|
+
}
|
|
298
|
+
|
|
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
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
229
359
|
.stat-grid {
|
|
230
360
|
display: grid;
|
|
231
361
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
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
|
}
|