@justai/cuts 0.49.0 → 0.51.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 +1 -1
- package/src/Base.astro +2 -2
- package/src/Posts.astro +52 -8
package/package.json
CHANGED
package/src/Base.astro
CHANGED
|
@@ -66,7 +66,7 @@ interface Props {
|
|
|
66
66
|
themeLight?: string;
|
|
67
67
|
/** app.zone's property by default. justai.pro passes its own. Pass `null` for a persona that runs
|
|
68
68
|
* NO analytics at all — the whole GA block and the consent banner are then not rendered. The
|
|
69
|
-
* privacy/terms
|
|
69
|
+
* privacy/terms child personas use this: the surface that proves it collects nothing by running
|
|
70
70
|
* nothing. */
|
|
71
71
|
gaId?: string | null;
|
|
72
72
|
}
|
|
@@ -200,7 +200,7 @@ const localePath = (l: string) => (l === defaultLocale ? "/" : `/${l}/`);
|
|
|
200
200
|
request and no console error, which is the only kind of analytics bug nobody finds. */}
|
|
201
201
|
{/* gaId can be null: a persona that measures NOTHING. Pass gaId={null} and the entire analytics
|
|
202
202
|
block below and the consent banner in the body are simply not rendered — no gtag, no cookie,
|
|
203
|
-
no request. The privacy/terms
|
|
203
|
+
no request. The privacy/terms child personas run this way, and it is not an exception but their
|
|
204
204
|
character: the one surface that can prove it collects nothing by running nothing (doctrine 15).
|
|
205
205
|
The default stays app.zone's property, so every existing persona is unchanged — this costs the
|
|
206
206
|
ten that measure themselves exactly nothing. */}
|
package/src/Posts.astro
CHANGED
|
@@ -21,12 +21,20 @@
|
|
|
21
21
|
// folding it UP into this cut is what lets every persona show a cross-reply correctly instead of
|
|
22
22
|
// mislabelling it as its own.
|
|
23
23
|
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
24
|
+
// EVERYONE IS NAMED ONCE — no @handle anywhere in this feed.
|
|
25
|
+
//
|
|
26
|
+
// It started with the personas, where the name and the handle are the same word by construction: app.zone
|
|
27
|
+
// beside @appzone, AMRAP beside @amrap, Tabata beside @tabata. One reads as identity; six down a thread read
|
|
28
|
+
// as stutter, and on a 480px column each one spent width saying nothing new. The self-reply had always
|
|
29
|
+
// rendered as `name · time`, so dropping the rest just made the family read the way the host already did.
|
|
30
|
+
//
|
|
31
|
+
// Humans were kept for one release on the argument that a person's display name and their account are two
|
|
32
|
+
// different facts. Overruled, and the call is better than the argument was: what a reader needs from a line
|
|
33
|
+
// in a conversation is who said it, and the name is that. The @handle was a convention borrowed from a
|
|
34
|
+
// product where handles are how you address someone — nobody is addressed here.
|
|
35
|
+
//
|
|
36
|
+
// `handle` (the prop) and `authorHandle` (the API field) both stay: they are keys, not labels. The feed
|
|
37
|
+
// query, the is-this-mine test, and the link to another persona's profile all read them. Nothing prints them.
|
|
30
38
|
interface Props {
|
|
31
39
|
handle: string; // the persona KEY — the feed query (?persona=) and the "is this reply mine?" test
|
|
32
40
|
name: string; // the persona's display name; rendered RAW, so a name may carry markup (justai's ".pro")
|
|
@@ -98,7 +106,43 @@ const vars = [
|
|
|
98
106
|
.trow.last .cc { padding-block-end: 0; }
|
|
99
107
|
|
|
100
108
|
.pa { flex: none; width: 42px; height: 42px; border-radius: var(--radius-sm); display: grid; place-items: center; font-size: 22px; background: var(--feed-av-bg, #fff); border: 1px solid var(--border); overflow: hidden; }
|
|
101
|
-
|
|
109
|
+
/* A human's avatar: their initial on a disc in their own colour, which arrives from the API as --c.
|
|
110
|
+
It was the last flat-filled object in the family, so it takes the house FILL recipe
|
|
111
|
+
(app-zone/.justai/expert/how-an-app-zone-icon-is-lit.md): an off-centre radial at 37% 31% with three
|
|
112
|
+
stops at L+22 / L+4 / L−11, so the light falls from the upper left and the lower right carries the
|
|
113
|
+
shade.
|
|
114
|
+
|
|
115
|
+
THE SPECULAR IS DELIBERATELY OMITTED, which is the one place this departs from the recipe. The doc
|
|
116
|
+
defines it as "what makes it a sphere rather than a lighter disc" — exactly right for howlong's beads,
|
|
117
|
+
which are beads, and wrong here. With it the disc read as a ball; without it the same three stops read
|
|
118
|
+
as a flat disc with light across it, which is what an avatar should be. Measured on Dana's #0a84ff: the
|
|
119
|
+
highlight sits 63 luminance steps above the flat colour instead of 94, and the rim is untouched at L99
|
|
120
|
+
either way — only the middle came down.
|
|
121
|
+
|
|
122
|
+
The stops are derived in CSS rather than precomputed, because the colour only exists at runtime.
|
|
123
|
+
`hsl(from var(--c) h s calc(l ± n))` is the rule stated literally: hue and saturation are passed
|
|
124
|
+
through untouched and lightness does all the shading. Measured — lit end 210.2° S100, shaded end
|
|
125
|
+
210.1° S100, so nothing drifts.
|
|
126
|
+
|
|
127
|
+
NO % ON THOSE NUMBERS. Inside relative colour syntax `l` resolves to a <number>, so `calc(l + 22%)`
|
|
128
|
+
mixes types, the declaration is dropped, and the disc silently renders flat — it looked finished and
|
|
129
|
+
measured as zero light-to-shadow difference until the units came off. `background-color` is kept as a
|
|
130
|
+
separate declaration for the same reason: where the syntax is unsupported the disc loses its light
|
|
131
|
+
rather than disappearing.
|
|
132
|
+
|
|
133
|
+
`max(0, 39 - l)` is the doc's L28 floor as a LIFT applied to all three stops, not a clamp on the dark
|
|
134
|
+
end: clamping compresses the spread and weakens the emboss unevenly. 39 is the shortfall threshold
|
|
135
|
+
(28 + 11), so a colour light enough to clear it adds nothing. */
|
|
136
|
+
.uav {
|
|
137
|
+
flex: none; width: 30px; height: 30px; border-radius: 50%;
|
|
138
|
+
display: grid; place-items: center; font-size: 13px; font-weight: 600; color: #fff;
|
|
139
|
+
background-color: var(--c, #888);
|
|
140
|
+
background-image:
|
|
141
|
+
radial-gradient(ellipse 80% 80% at 37% 31%,
|
|
142
|
+
hsl(from var(--c, #888) h s calc(l + 22 + max(0, 39 - l))) 0%,
|
|
143
|
+
hsl(from var(--c, #888) h s calc(l + 4 + max(0, 39 - l))) 40%,
|
|
144
|
+
hsl(from var(--c, #888) h s calc(l - 11 + max(0, 39 - l))) 100%);
|
|
145
|
+
}
|
|
102
146
|
.rav { flex: none; width: 30px; height: 30px; border-radius: var(--radius-sm); display: grid; place-items: center; font-size: 15px; background: var(--feed-av-bg, #fff); border: 1px solid var(--border); overflow: hidden; }
|
|
103
147
|
.pa :global(svg), .rav :global(svg) { display: block; }
|
|
104
148
|
.pa .mk, .rav .mk { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
@@ -242,7 +286,7 @@ const vars = [
|
|
|
242
286
|
return `<div class="trow${last ? " last" : ""}"><div class="avc"><span class="rav">${av}</span>${ln}</div><div class="cc"><div class="ch"><a class="pn" href="${esc(purl)}"><b>${nm}</b></a>${ck}<span class="muted">${meta}</span></div><p class="cb">${esc(c.body)}</p></div></div>`;
|
|
243
287
|
}
|
|
244
288
|
const initial = esc(String(c.authorName || "?").slice(0, 1));
|
|
245
|
-
return `<div class="trow${last ? " last" : ""}"><div class="avc"><span class="uav" style="--c:${esc(c.authorColor || "#888")}">${initial}</span>${ln}</div><div class="cc"><div class="ch"><b>${esc(c.authorName)}</b><span class="muted"
|
|
289
|
+
return `<div class="trow${last ? " last" : ""}"><div class="avc"><span class="uav" style="--c:${esc(c.authorColor || "#888")}">${initial}</span>${ln}</div><div class="cc"><div class="ch"><b>${esc(c.authorName)}</b><span class="muted">· ${timeAgo(c.createdAt)}</span></div><p class="cb">${esc(c.body)}</p></div></div>`;
|
|
246
290
|
};
|
|
247
291
|
const post = (p: P): string => {
|
|
248
292
|
const cs = p.comments || [];
|