driggsby 0.7.2 → 0.7.3
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/template-styles.js +70 -12
- package/dist/init/templates.js +66 -9
- package/package.json +3 -3
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
// (init-command.test.ts pins the two together): Driggsby paints that color
|
|
12
12
|
// while the app loads.
|
|
13
13
|
export const STYLES_CSS = `/* Driggsby's console palette, so this page reads as part of the app it
|
|
14
|
-
opens inside. Change these together if you re-theme
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
opens inside. Change these together if you re-theme (all but
|
|
15
|
+
--font-sans, which stays as it is); keep --bg-app equal to the
|
|
16
|
+
"background" in driggsby.json. The status tones are the console's own,
|
|
17
|
+
for gains, losses, warnings, and a live dot. */
|
|
17
18
|
:root {
|
|
18
19
|
color-scheme: dark;
|
|
19
20
|
--bg-app: #000000;
|
|
@@ -36,13 +37,16 @@ export const STYLES_CSS = `/* Driggsby's console palette, so this page reads as
|
|
|
36
37
|
|
|
37
38
|
/* Inter, which Driggsby serves at /-/inter.woff2 on this app's own origin
|
|
38
39
|
(driggsby dev serves the same file there), so nothing loads from another
|
|
39
|
-
site.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
site. Inter is the only font a dashboard uses: set all text with
|
|
41
|
+
var(--font-sans), keep that stack as it is (the faces after Inter only
|
|
42
|
+
stand in if the font is slow or fails to load, or lacks a character),
|
|
43
|
+
and name no other font anywhere else. A <canvas> or a chart library's
|
|
44
|
+
font setting can't read var(): name "Inter" there, followed only by
|
|
45
|
+
sans-serif, and draw a canvas after document.fonts.ready. The
|
|
46
|
+
Dashboards page shows a picture of this page, taken on a server with
|
|
47
|
+
none of your fonts, and any other face looks different there than on
|
|
48
|
+
the live page. Text waits for the font (font-display: block,
|
|
49
|
+
preloaded) rather than flash another face first. */
|
|
46
50
|
@font-face {
|
|
47
51
|
font-family: "Inter";
|
|
48
52
|
src: url("/-/inter.woff2") format("woff2");
|
|
@@ -65,6 +69,15 @@ body {
|
|
|
65
69
|
-webkit-font-smoothing: antialiased;
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
/* Browsers set form controls in the system font on their own, whatever
|
|
73
|
+
the body's; these take the page's Inter instead. */
|
|
74
|
+
button,
|
|
75
|
+
input,
|
|
76
|
+
select,
|
|
77
|
+
textarea {
|
|
78
|
+
font: inherit;
|
|
79
|
+
}
|
|
80
|
+
|
|
68
81
|
/* A compact column: a few figures and a short list read best close
|
|
69
82
|
together. The console's own pages run wider (75rem); widen this as the
|
|
70
83
|
page grows. */
|
|
@@ -114,6 +127,10 @@ h2 {
|
|
|
114
127
|
}
|
|
115
128
|
|
|
116
129
|
.stat {
|
|
130
|
+
/* The figure inside sizes itself to this card (see .stat-value), so
|
|
131
|
+
the card takes its width from the grid, never from its content: in a
|
|
132
|
+
content-sized track it would collapse. */
|
|
133
|
+
container-type: inline-size;
|
|
117
134
|
display: flex;
|
|
118
135
|
flex-direction: column;
|
|
119
136
|
padding: 14px 16px;
|
|
@@ -129,15 +146,34 @@ h2 {
|
|
|
129
146
|
|
|
130
147
|
.stat-value {
|
|
131
148
|
margin-top: 2px;
|
|
149
|
+
/* 20px, and 20px wherever container units aren't known; the @supports
|
|
150
|
+
rule below shrinks a figure too long for its card. The line stays
|
|
151
|
+
30px whatever size the figure draws at, so it measures the same as
|
|
152
|
+
the skeleton bar it replaces. */
|
|
132
153
|
font-size: 20px;
|
|
154
|
+
line-height: 30px;
|
|
133
155
|
font-weight: 600;
|
|
134
156
|
letter-spacing: -0.01em;
|
|
135
157
|
font-variant-numeric: tabular-nums;
|
|
136
|
-
/*
|
|
137
|
-
|
|
158
|
+
/* Past the 12px floor a figure wraps rather than spill out of its
|
|
159
|
+
card: a clipped balance is a wrong number. */
|
|
138
160
|
overflow-wrap: anywhere;
|
|
139
161
|
}
|
|
140
162
|
|
|
163
|
+
/* A figure keeps 20px unless it is too long for its card, and then
|
|
164
|
+
shrinks just enough to stay one number: its length (--chars, which
|
|
165
|
+
renderOverview sets) at 0.6em a character, the widest these figures
|
|
166
|
+
run with a little room, fills at most the card (100cqi). A short
|
|
167
|
+
balance never shrinks; $99,999,999.99 in a desktop card draws near
|
|
168
|
+
17px. Inside @supports, so a browser without container units keeps
|
|
169
|
+
20px: outside it, the var() would let the line override 20px and
|
|
170
|
+
then fail. */
|
|
171
|
+
@supports (width: 1cqi) {
|
|
172
|
+
.stat-value {
|
|
173
|
+
font-size: clamp(12px, calc(100cqi / (var(--chars, 1) * 0.6)), 20px);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
141
177
|
#accounts {
|
|
142
178
|
border: 1px solid var(--hairline);
|
|
143
179
|
border-radius: var(--radius-card);
|
|
@@ -162,6 +198,28 @@ h2 {
|
|
|
162
198
|
color: var(--text-secondary);
|
|
163
199
|
}
|
|
164
200
|
|
|
201
|
+
/* A section whose call failed says why in its own place (see "When a call
|
|
202
|
+
fails" in app.js): in the list, in a row's inset under any rows it
|
|
203
|
+
keeps; across the stat row, as a card of its own. A long reason (one
|
|
204
|
+
can quote your SQL) wraps inside it. */
|
|
205
|
+
.problem {
|
|
206
|
+
padding: 14px 16px;
|
|
207
|
+
color: var(--text-warning);
|
|
208
|
+
overflow-wrap: anywhere;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.stat-grid > .problem {
|
|
212
|
+
grid-column: 1 / -1;
|
|
213
|
+
border: 1px solid var(--hairline);
|
|
214
|
+
border-radius: var(--radius-card);
|
|
215
|
+
background: var(--bg-card);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.account-row + .problem,
|
|
219
|
+
.empty-row + .problem {
|
|
220
|
+
border-top: 1px solid var(--hairline);
|
|
221
|
+
}
|
|
222
|
+
|
|
165
223
|
/* The institution's initial on a round pill, as the console draws it. */
|
|
166
224
|
.account-glyph {
|
|
167
225
|
flex: none;
|
package/dist/init/templates.js
CHANGED
|
@@ -88,11 +88,13 @@ export const APP_JS = `// Your app's code. Edit anything — driggsby dev reload
|
|
|
88
88
|
//
|
|
89
89
|
// Reading data is one call:
|
|
90
90
|
//
|
|
91
|
-
// driggsby.watch(tool, params, callback)
|
|
91
|
+
// driggsby.watch(tool, params, callback, { onError })
|
|
92
92
|
//
|
|
93
93
|
// Each watch is a live subscription: the callback runs with a fresh result
|
|
94
|
-
// whenever your Driggsby data changes
|
|
95
|
-
//
|
|
94
|
+
// whenever your Driggsby data changes, and onError runs instead when a call
|
|
95
|
+
// fails (see "When a call fails" below; watchSection wires one section
|
|
96
|
+
// that way, so add yours with it). The sample data below matches the real
|
|
97
|
+
// result shapes, so the render functions work unchanged either way.
|
|
96
98
|
//
|
|
97
99
|
// These are the tools an app can watch — read-only, nothing else responds:
|
|
98
100
|
//
|
|
@@ -204,10 +206,11 @@ function renderOverview(result) {
|
|
|
204
206
|
];
|
|
205
207
|
for (const [label, value] of stats) {
|
|
206
208
|
const stat = element("div", "stat");
|
|
207
|
-
stat
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
);
|
|
209
|
+
const figure = element("div", "stat-value", formatMoney(value));
|
|
210
|
+
// Its length, so a long figure can shrink to fit its card while a
|
|
211
|
+
// short one keeps its full size (see .stat-value in styles.css).
|
|
212
|
+
figure.style.setProperty("--chars", String(figure.textContent.length));
|
|
213
|
+
stat.append(element("div", "stat-label", label), figure);
|
|
211
214
|
container.append(stat);
|
|
212
215
|
}
|
|
213
216
|
// aria-busy ships in the HTML so assistive tech hears "loading" until
|
|
@@ -348,6 +351,60 @@ function settledRenderer(render) {
|
|
|
348
351
|
};
|
|
349
352
|
}
|
|
350
353
|
|
|
354
|
+
// ---------------------------------------------------------------------------
|
|
355
|
+
// When a call fails. A watch whose call fails never runs its callback; it
|
|
356
|
+
// runs onError instead, with { message, kind }, once for each new failure —
|
|
357
|
+
// after Driggsby has already retried a call that failed because it was busy
|
|
358
|
+
// or slow. The section says why where its data would be: in place of its
|
|
359
|
+
// skeleton, so a failure never looks like a page still loading, or under
|
|
360
|
+
// the data it already shows, which stays. The watch asks again on its own
|
|
361
|
+
// the next time your data changes, and the next good result repaints the
|
|
362
|
+
// section, reason and all. A section counts as still loading while its
|
|
363
|
+
// container is aria-busy="true": the HTML ships every container that way,
|
|
364
|
+
// and every render clears it and replaces everything in its container,
|
|
365
|
+
// so keep all three true for a section you add.
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
|
|
368
|
+
function showProblem(containerId, error) {
|
|
369
|
+
const container = document.getElementById(containerId);
|
|
370
|
+
const message =
|
|
371
|
+
error && typeof error.message === "string" && error.message
|
|
372
|
+
? error.message
|
|
373
|
+
: "This didn't load. It tries again when your data next changes.";
|
|
374
|
+
const note = element("div", "problem", message);
|
|
375
|
+
note.setAttribute("role", "status");
|
|
376
|
+
if (container.getAttribute("aria-busy") === "true") {
|
|
377
|
+
container.replaceChildren(note);
|
|
378
|
+
container.removeAttribute("aria-busy");
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const earlier = container.querySelector(".problem");
|
|
382
|
+
if (earlier) earlier.remove();
|
|
383
|
+
container.append(note);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// One section's live data: its watch, its render, and its failures. A
|
|
387
|
+
// failure that lands while the section's first result waits for its
|
|
388
|
+
// dissolve still shows once that result paints, under it. It returns the
|
|
389
|
+
// watch's stop function: call it before watching the same section again
|
|
390
|
+
// with new params.
|
|
391
|
+
function watchSection(tool, params, containerId, render) {
|
|
392
|
+
let problem = null;
|
|
393
|
+
const paint = settledRenderer((result) => {
|
|
394
|
+
render(result);
|
|
395
|
+
if (problem) showProblem(containerId, problem);
|
|
396
|
+
});
|
|
397
|
+
return driggsby.watch(tool, params, (result) => {
|
|
398
|
+
problem = null;
|
|
399
|
+
paint(result);
|
|
400
|
+
}, {
|
|
401
|
+
onError: (error) => {
|
|
402
|
+
problem = error;
|
|
403
|
+
showProblem(containerId, error);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
351
408
|
// ---------------------------------------------------------------------------
|
|
352
409
|
// Live data. window.driggsby exists when the Driggsby SDK loaded — inside
|
|
353
410
|
// Driggsby, or under driggsby dev. Whether anything embeds this page is
|
|
@@ -367,8 +424,8 @@ if (standalone) {
|
|
|
367
424
|
}
|
|
368
425
|
|
|
369
426
|
if (window.driggsby) {
|
|
370
|
-
|
|
371
|
-
|
|
427
|
+
watchSection("get_overview", {}, "overview", renderOverview);
|
|
428
|
+
watchSection("list_accounts", {}, "accounts", renderAccounts);
|
|
372
429
|
}
|
|
373
430
|
`;
|
|
374
431
|
// "background" is the template's own page background (styles.css
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "driggsby",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "Set up and build on Driggsby, the MCP server for personal finance.",
|
|
5
5
|
"license": "Apache-2.0 AND OFL-1.1",
|
|
6
6
|
"repository": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"node": ">=18"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@driggsby/deploy": "0.7.
|
|
34
|
-
"@driggsby/sdk": "0.7.
|
|
33
|
+
"@driggsby/deploy": "0.7.3",
|
|
34
|
+
"@driggsby/sdk": "0.7.3"
|
|
35
35
|
}
|
|
36
36
|
}
|