@justai/cuts 0.133.0 → 0.138.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/package.json +6 -3
- package/src/ActionList.astro +31 -27
- package/src/ActionRow.astro +68 -64
- package/src/BackToZone.astro +39 -35
- package/src/Base.astro +21 -3
- package/src/Catalogue.astro +95 -319
- package/src/CatalogueCard.astro +72 -68
- package/src/ChildPersona.astro +55 -51
- package/src/ConfirmSheet.astro +69 -65
- package/src/FilePicker.astro +21 -378
- package/src/LangPicker.astro +49 -104
- package/src/Posts.astro +177 -396
- package/src/Preroll.astro +204 -487
- package/src/Profile.astro +39 -35
- package/src/Segmented.astro +62 -111
- package/src/Sheet.astro +247 -507
- package/src/Toolbar.astro +288 -362
- package/src/behaviour/catalogue.ts +239 -0
- package/src/behaviour/filePicker.ts +372 -0
- package/src/behaviour/langPicker.ts +70 -0
- package/src/behaviour/posts.ts +234 -0
- package/src/behaviour/preroll.ts +298 -0
- package/src/behaviour/segmented.ts +64 -0
- package/src/behaviour/sheet.ts +300 -0
- package/src/behaviour/toolbar.ts +89 -0
- package/src/ground.css +124 -120
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@justai/cuts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.138.0",
|
|
4
4
|
"description": "A persona's named parts \u2014 the page shell that holds them, and the cuts themselves.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -41,8 +41,11 @@
|
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@justai/core": "^0.
|
|
45
|
-
"@justai/ui": "^0.
|
|
44
|
+
"@justai/core": "^0.36.0",
|
|
45
|
+
"@justai/ui": "^0.25.0",
|
|
46
46
|
"@justai/atrium": "^0.1.9"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"check": "bunx tsc -p tsconfig.json"
|
|
47
50
|
}
|
|
48
51
|
}
|
package/src/ActionList.astro
CHANGED
|
@@ -45,32 +45,36 @@ const { title, note } = Astro.props;
|
|
|
45
45
|
</div>
|
|
46
46
|
|
|
47
47
|
<style>
|
|
48
|
-
/*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
48
|
+
/* Scoped by Astro's hash AND placed in the cascade: a persona overriding this never has to
|
|
49
|
+
out-specify it. See @justai/ui/tokens.css for the order, and for what deliberately stays out. */
|
|
50
|
+
@layer justai.components {
|
|
51
|
+
/* queries: two action lists touching · no gap between groups · the second group sits on the first
|
|
52
|
+
THE BREAK IS THE GRAMMAR, so the component owns it.
|
|
53
|
+
A group carried its INTERNAL 7px and nothing between itself and the next one, which left the
|
|
54
|
+
break — the thing that separates "things you might do" from "the door with no way back" — to
|
|
55
|
+
whichever consumer remembered a wrapper. files did in one sheet and not in another, and the
|
|
56
|
+
second one read as one long list with a red row at the end.
|
|
57
|
+
`:last-child` so a lone group adds no trailing space it did not ask for. */
|
|
58
|
+
.jal-group { display: flex; flex-direction: column; gap: 7px; }
|
|
59
|
+
.jal-group + .jal-group { margin-block-start: 22px; }
|
|
60
|
+
/* Inset and grouped: the card is the group, and `overflow: hidden` is what lets a row's own
|
|
61
|
+
background reach the rounded corner instead of squaring it off on press. */
|
|
62
|
+
.jal {
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-direction: column;
|
|
65
|
+
background: var(--surface-2);
|
|
66
|
+
border: 1px solid var(--border);
|
|
67
|
+
border-radius: var(--radius-md);
|
|
68
|
+
overflow: hidden;
|
|
69
|
+
}
|
|
70
|
+
.jal-title {
|
|
71
|
+
font-size: var(--fs-xs);
|
|
72
|
+
font-weight: 600;
|
|
73
|
+
color: var(--text-dim);
|
|
74
|
+
text-transform: uppercase;
|
|
75
|
+
letter-spacing: 0.04em;
|
|
76
|
+
padding-inline: 4px;
|
|
77
|
+
}
|
|
78
|
+
.jal-note { font-size: var(--fs-xs); color: var(--text-dim); padding-inline: 4px; line-height: 1.45; }
|
|
66
79
|
}
|
|
67
|
-
.jal-title {
|
|
68
|
-
font-size: var(--fs-xs);
|
|
69
|
-
font-weight: 600;
|
|
70
|
-
color: var(--text-dim);
|
|
71
|
-
text-transform: uppercase;
|
|
72
|
-
letter-spacing: 0.04em;
|
|
73
|
-
padding-inline: 4px;
|
|
74
|
-
}
|
|
75
|
-
.jal-note { font-size: var(--fs-xs); color: var(--text-dim); padding-inline: 4px; line-height: 1.45; }
|
|
76
80
|
</style>
|
package/src/ActionRow.astro
CHANGED
|
@@ -69,72 +69,76 @@ const { id, label, tone = "plain", detail, hidden, i18n, ...rest } = Astro.props
|
|
|
69
69
|
</button>
|
|
70
70
|
|
|
71
71
|
<style>
|
|
72
|
-
/*
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
.jal-row
|
|
101
|
-
|
|
102
|
-
|
|
72
|
+
/* Scoped by Astro's hash AND placed in the cascade: a persona overriding this never has to
|
|
73
|
+
out-specify it. See @justai/ui/tokens.css for the order, and for what deliberately stays out. */
|
|
74
|
+
@layer justai.components {
|
|
75
|
+
/* A ROW, not a button in a line: it takes the full width it is given, so the label is never the
|
|
76
|
+
thing that has to shrink. The 44px floor is the one number iOS never moves — it is a fingertip,
|
|
77
|
+
and `min-height` rather than a fixed height so a reader who raised their text size gets a
|
|
78
|
+
taller row instead of a clipped one. */
|
|
79
|
+
.jal-row {
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
gap: 12px;
|
|
83
|
+
width: 100%;
|
|
84
|
+
min-height: 44px;
|
|
85
|
+
padding: 11px 14px;
|
|
86
|
+
border: 0;
|
|
87
|
+
background: none;
|
|
88
|
+
color: var(--text);
|
|
89
|
+
font: inherit;
|
|
90
|
+
font-size: var(--fs-sm);
|
|
91
|
+
font-weight: 500;
|
|
92
|
+
text-align: start;
|
|
93
|
+
cursor: pointer;
|
|
94
|
+
/* The separator lives on the row and is removed from the last one by the list — so a row can be
|
|
95
|
+
hidden or revealed without leaving a hairline hanging under nothing. */
|
|
96
|
+
border-block-end: 1px solid var(--border);
|
|
97
|
+
transition: background 0.15s;
|
|
98
|
+
}
|
|
99
|
+
.jal-row:last-child,
|
|
100
|
+
.jal-row[hidden] + .jal-row:last-child { border-block-end: 0; }
|
|
101
|
+
.jal-row:active { background: color-mix(in srgb, var(--text) 8%, transparent); }
|
|
102
|
+
@media (hover: hover) {
|
|
103
|
+
.jal-row:hover { background: color-mix(in srgb, var(--text) 5%, transparent); }
|
|
104
|
+
}
|
|
105
|
+
.jal-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
103
106
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
.jal-ico {
|
|
108
|
+
display: inline-flex;
|
|
109
|
+
flex: none;
|
|
110
|
+
color: var(--text-dim);
|
|
111
|
+
}
|
|
112
|
+
/* The glyph is cut to a fixed box, so it is sized in px on purpose — it is a mark, not reading
|
|
113
|
+
text, and the box it sits in is a hand size rather than a type size. */
|
|
114
|
+
.jal-ico :global(svg) { width: 20px; height: 20px; display: block; }
|
|
112
115
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
/* The label takes the room. This is the whole point of the component: nothing here clips. */
|
|
117
|
+
.jal-label { flex: 1; min-width: 0; }
|
|
118
|
+
.jal-detail { color: var(--text-dim); font-size: var(--fs-xs); flex: none; }
|
|
116
119
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
120
|
+
/* queries: the accent row is unreadable in light mode · is-accent contrast · accent as text ·
|
|
121
|
+
my persona's accent is too bright to read in a sheet
|
|
122
|
+
THE ACCENT AS INK IS NOT THE ACCENT AS PAINT. A fill only has to be SEEN; a word has to be READ,
|
|
123
|
+
and a persona whose accent is light fails contrast here while looking perfect as a button — tts's
|
|
124
|
+
amber measures 1.67:1 on the light surface against the 4.5 a word needs.
|
|
125
|
+
This is the same problem ConfirmSheet already solved with `--confirm-accent`, one component over,
|
|
126
|
+
for the same stated reason ("the action reads as TEXT"). The hook is additive: a consumer that
|
|
127
|
+
sets nothing gets exactly what it got before, and one that sets `--accent-text` gets a readable
|
|
128
|
+
row. Prefer fixing it here over a persona globalising this class — that is what tts had to do
|
|
129
|
+
while this shipped, and a persona reaching into a cut's internals is a fix that rots. */
|
|
130
|
+
.jal-row.is-accent { color: var(--accent-text, var(--accent)); font-weight: 600; }
|
|
131
|
+
.jal-row.is-accent .jal-ico { color: var(--accent-text, var(--accent)); }
|
|
132
|
+
.jal-row.is-danger { color: var(--danger); }
|
|
133
|
+
.jal-row.is-danger .jal-ico { color: var(--danger); }
|
|
134
|
+
.jal-row.is-danger:active { background: color-mix(in srgb, var(--danger) 12%, transparent); }
|
|
135
|
+
@media (hover: hover) {
|
|
136
|
+
.jal-row.is-danger:hover { background: color-mix(in srgb, var(--danger) 10%, transparent); }
|
|
137
|
+
}
|
|
135
138
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
139
|
+
.jal-row[disabled] { opacity: 0.4; cursor: default; }
|
|
140
|
+
/* A class-set display beats the user agent's `[hidden] { display: none }` — that has bitten this
|
|
141
|
+
ecosystem twice already, in this very sheet. Said again where the display is set. */
|
|
142
|
+
.jal-row[hidden] { display: none !important; }
|
|
143
|
+
}
|
|
140
144
|
</style>
|
package/src/BackToZone.astro
CHANGED
|
@@ -92,44 +92,48 @@ const href = `https://${apex}`;
|
|
|
92
92
|
</a>
|
|
93
93
|
|
|
94
94
|
<style>
|
|
95
|
-
/*
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
95
|
+
/* Scoped by Astro's hash AND placed in the cascade: a persona overriding this never has to
|
|
96
|
+
out-specify it. See @justai/ui/tokens.css for the order, and for what deliberately stays out. */
|
|
97
|
+
@layer justai.components {
|
|
98
|
+
/* icon-only, flat — flush to the left edge of the (full-bleed) toolbar */
|
|
99
|
+
.home {
|
|
100
|
+
display: inline-grid; place-items: center; width: 36px; height: 36px;
|
|
101
|
+
color: var(--text-dim); text-decoration: none; margin-inline-start: -7px;
|
|
102
|
+
transition: color 0.2s, transform 0.2s var(--ease-spring);
|
|
103
|
+
}
|
|
104
|
+
.home:hover { color: var(--text); }
|
|
105
|
+
.home:active { transform: scale(0.9); }
|
|
106
|
+
.home svg { width: 20px; height: 20px; display: block; }
|
|
104
107
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
/* At rest every part of the mark is the row's ink, so the silhouette reads as one shape rather than
|
|
109
|
+
a logo parked in a toolbar. The centre is FILLED rather than knocked out: in the coloured drawing
|
|
110
|
+
it is a counter-shape separating four saturated arms, and in one colour there is nothing to
|
|
111
|
+
separate — a hole there would be a gap the mark does not have. */
|
|
112
|
+
.azmark .arm,
|
|
113
|
+
.azmark .ctr { fill: currentColor; }
|
|
111
114
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
/* queries: the home icon is smaller than the other glyphs · 20px of ink · pgmark size
|
|
116
|
+
FILLED, AND THE viewBox IS THE INK'S OWN BOUNDS. This mark was three round-capped strokes, which
|
|
117
|
+
is the same shape — but getBBox reports a stroke's centreline only, and the fleet's "every icon
|
|
118
|
+
paints 20px of ink" spec measured it at 9.9px beside 20px glyphs. The spec was right: at that
|
|
119
|
+
size it really was half its neighbours. Rotated rounded rects draw exactly what the strokes did
|
|
120
|
+
and measure what they look like. */
|
|
121
|
+
.pgmark .arm { fill: currentColor; }
|
|
122
|
+
.home:hover .pgmark .a1, .home:focus-visible .pgmark .a1 { fill: #3D6FD9; }
|
|
123
|
+
.home:hover .pgmark .a2, .home:focus-visible .pgmark .a2 { fill: #E8503A; }
|
|
124
|
+
.home:hover .pgmark .a3, .home:focus-visible .pgmark .a3 { fill: #F5A623; }
|
|
122
125
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
126
|
+
.home:hover .azmark .a1, .home:focus-visible .azmark .a1 { fill: url(#azHome1); }
|
|
127
|
+
.home:hover .azmark .a2, .home:focus-visible .azmark .a2 { fill: url(#azHome2); }
|
|
128
|
+
.home:hover .azmark .a3, .home:focus-visible .azmark .a3 { fill: url(#azHome3); }
|
|
129
|
+
.home:hover .azmark .a4, .home:focus-visible .azmark .a4 { fill: url(#azHome4); }
|
|
130
|
+
/* The centre follows the ground, and both values are the project's own: #F5F1E8 is the centre of
|
|
131
|
+
Logo Black, drawn for dark grounds, and #2B2724 the centre of Logo White, drawn for pale ones.
|
|
132
|
+
Dark is the un-queried default because the fleet's tokens are dark-first. */
|
|
133
|
+
.home:hover .azmark .ctr, .home:focus-visible .azmark .ctr { fill: #F5F1E8; }
|
|
134
|
+
@media (prefers-color-scheme: light) {
|
|
135
|
+
:global(html:not([data-theme="dark"])) .home:hover .azmark .ctr, :global(html:not([data-theme="dark"])) .home:focus-visible .azmark .ctr { fill: #2B2724; }
|
|
136
|
+
}
|
|
133
137
|
}
|
|
134
138
|
</style>
|
|
135
139
|
|
package/src/Base.astro
CHANGED
|
@@ -33,7 +33,7 @@ import { dir as dirOf } from "@justai/ui/locale";
|
|
|
33
33
|
import "./ground.css";
|
|
34
34
|
// The load-bearing behaviour is framework-free and lives in @justai/core, so `feed` (Next) injects the
|
|
35
35
|
// SAME strings instead of hand-copying them. This binding just decides where they go in an Astro head.
|
|
36
|
-
import { AUTH_CAPTURE_JS, CONSENT_JS, JS_CLASS_JS, REVEAL_CSS,
|
|
36
|
+
import { AUTH_CAPTURE_JS, CONSENT_JS, JS_CLASS_JS, REVEAL_CSS, analyticsJs, consentCopy } from "@justai/core";
|
|
37
37
|
// The consent ask IS a confirmation — a moment of trust — so it rides the fleet's ONE dialog
|
|
38
38
|
// rather than keeping a private dialect. One anatomy for "are you sure?" and "may I count you?".
|
|
39
39
|
import ConfirmSheet from "./ConfirmSheet.astro";
|
|
@@ -180,8 +180,26 @@ const localePath = (l: string) => (l === defaultLocale ? "/" : `/${l}/`);
|
|
|
180
180
|
repo is one nobody has when they need it.
|
|
181
181
|
It is glasses, not a renovation: nothing is added to the page's own elements, the overlay is
|
|
182
182
|
fixed and reads `#kernel`'s rect, and `pointer-events` is none throughout. And it costs
|
|
183
|
-
nothing when off —
|
|
184
|
-
|
|
183
|
+
nothing when off — MEASURED, and it did not. */}
|
|
184
|
+
{/* queries: xray page weight · why is the x-ray a dynamic import · debug tool on every page ·
|
|
185
|
+
5381 bytes of xray · lazy load a debug overlay
|
|
186
|
+
|
|
187
|
+
IT COST 5,381 BYTES ON EVERY PAGE OF EVERY PERSONA, TO EVERY VISITOR — 10 to 16% of the
|
|
188
|
+
document, measured on four live hosts. "It costs nothing when off" was true of the RUNTIME
|
|
189
|
+
(one `if` and a return) and false of the TRANSFER, and only one of those is what a visitor
|
|
190
|
+
pays. The whole overlay was inlined into the head so that a branch could decline to run it.
|
|
191
|
+
|
|
192
|
+
So the flag check stays inline and the instrument is fetched only by whoever asked for it.
|
|
193
|
+
The person debugging pays one request; everybody else pays the bytes below. Astro splits the
|
|
194
|
+
dynamic import into its own chunk, so nothing about it reaches a page that does not ask.
|
|
195
|
+
|
|
196
|
+
It is a real module now, not a string — see @justai/core/xray. */}
|
|
197
|
+
<script>
|
|
198
|
+
if (new URLSearchParams(location.search).has("xray")) {
|
|
199
|
+
const { xray } = await import("@justai/core/xray");
|
|
200
|
+
xray();
|
|
201
|
+
}
|
|
202
|
+
</script>
|
|
185
203
|
{/* Reveal only after a page marks itself ready — gated behind .js so the page is always visible
|
|
186
204
|
if JS is off, and no scroll flash before we position at the Kernel. This style is a TRAP as
|
|
187
205
|
much as a feature, and the warning must stay beside it: ANY page using this layout without a
|