@justai/cuts 0.142.0 → 0.144.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justai/cuts",
3
- "version": "0.142.0",
3
+ "version": "0.144.0",
4
4
  "description": "A persona's named parts — the page shell that holds them, and the cuts themselves.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -90,7 +90,10 @@ const icon = `${app.url}/icon.svg`;
90
90
  width: 60px;
91
91
  height: 60px;
92
92
  border-radius: var(--radius-md);
93
- object-fit: cover;
93
+ /* contain: this is a persona's mark, not a photograph. A square tile is unaffected — the two
94
+ declarations agree on square art — and a mark taller than its box keeps its ends instead of
95
+ having them cropped. See the note on .pa .mk in Posts.astro. */
96
+ object-fit: contain;
94
97
  box-shadow: 0 8px 20px -12px rgba(0, 0, 0, 0.35);
95
98
  }
96
99
 
@@ -269,8 +269,13 @@ const chrome = face === "page";
269
269
  .pp[data-face="sheet"] .app { max-width: none; margin-inline: 0; border: 0; background: none;
270
270
  backdrop-filter: none; -webkit-backdrop-filter: none; }
271
271
 
272
- /* a mark fills its box; the picture inside it covers */
273
- .pp .mk { display: block; width: 100%; height: 100%; object-fit: cover; }
272
+ /* A MARK FILLS ITS BOX AND LOSES NOTHING DOING IT. This said `cover`, which fills by scaling to the
273
+ larger ratio and cropping the overflow off both edges. Square art renders identically either way —
274
+ which is why it survived on every child page — but pin.games' mark is 700.19 x 767.26, so `cover`
275
+ took 4.8% off its top and 4.8% off its bottom, and those are the blue bracket and the red one.
276
+ Same change, same reason, as .pa .mk in Posts.astro. The feed service carries a fork of this
277
+ stylesheet and had lost the rule entirely; it is `contain` there now too. */
278
+ .pp .mk { display: block; width: 100%; height: 100%; object-fit: contain; }
274
279
 
275
280
  /* Motion is never forced on somebody who asked it not to be — and the biggest effects, the ones a
276
281
  surface is proudest of, are exactly what that setting exists to suppress. */
@@ -16,6 +16,13 @@ import "./ground.css";
16
16
  // something can end, the safe choice wears the most confident face. iOS system red for
17
17
  // destruction. This is /me's "Erase everything?".
18
18
  //
19
+ // A THIRD CHOICE, when the safe answer is not "do nothing" (`safeActionLabel`) — Apple's own
20
+ // multi-action sheet. Some destructive questions have a way OUT of the loss rather than only a
21
+ // way around it: "your work isn't saved yet" is answered best by saving it, not by cancelling and
22
+ // then hunting for the Save button. That action joins the grouped card ABOVE the destructive one,
23
+ // because a sheet reads top to bottom and the preserving path is the one to meet first. Cancel
24
+ // stays detached and boldest — the anatomy does not change, it gains one row.
25
+ //
19
26
  // ASK (`ask` prop) — the alert/ATT genre, for INVITATIONS: both choices live INSIDE the grouped
20
27
  // card, stacked; the action wears the preferred bold face (Apple's preferredAction) and takes
21
28
  // the focus; the safe choice sits beneath it in calm regular weight — equal size, equal tap,
@@ -61,10 +68,19 @@ interface Props {
61
68
  * remembered as a choice, so the chosen no is stored while a slip-away stores nothing and the
62
69
  * ask returns next visit. */
63
70
  cancelValue?: string;
71
+ /** Optional THIRD choice: an action that PRESERVES rather than ends, placed above the main action
72
+ * inside the grouped card and painted as the preferred path. Omit it and the sheet is exactly
73
+ * what it always was — same DOM, same ids, same returnValues. */
74
+ safeActionLabel?: string;
75
+ /** What returnValue that third choice reports. Default "safe": three answers need three words. */
76
+ safeActionValue?: string;
64
77
  /** Anything else (data-* attributes, aria overrides) lands on the <dialog> itself. */
65
78
  [key: string]: unknown;
66
79
  }
67
- const { id, title, body, actionLabel, cancelLabel, destructive = false, ask = false, cancelValue = "", ...rest } = Astro.props;
80
+ const {
81
+ id, title, body, actionLabel, cancelLabel, destructive = false, ask = false, cancelValue = "",
82
+ safeActionLabel, safeActionValue = "safe", ...rest
83
+ } = Astro.props;
68
84
  ---
69
85
 
70
86
  <dialog class="confirm" id={id} aria-labelledby={title ? `${id}-t` : `${id}-b`} aria-describedby={`${id}-b`} {...rest}>
@@ -79,6 +95,9 @@ const { id, title, body, actionLabel, cancelLabel, destructive = false, ask = fa
79
95
  on a closed dialog, noise about a focus that was never going to happen. Focus is the
80
96
  OPENER's job now, at showModal time: ask → the invitation (CONSENT_JS does it),
81
97
  destructive → the cancel (the consumer does it). The genre decides, as before. */}
98
+ {safeActionLabel && (
99
+ <button class="confirm-act preferred" id={`${id}-safe`} value={safeActionValue}>{safeActionLabel}</button>
100
+ )}
82
101
  <button class:list={["confirm-act", { destructive, preferred: ask }]} id={`${id}-act`} value="confirm">{actionLabel}</button>
83
102
  {ask && <button class="confirm-act quiet" id={`${id}-cancel`} value={cancelValue} formnovalidate>{cancelLabel}</button>}
84
103
  </div>
package/src/Posts.astro CHANGED
@@ -268,7 +268,19 @@ const vars = [
268
268
  }
269
269
  .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; }
270
270
  .pa :global(svg), .rav :global(svg) { display: block; }
271
- .pa .mk, .rav .mk { width: 100%; height: 100%; object-fit: cover; display: block; }
271
+ /* queries: the avatar is cropped · blue and red pieces cut off · pin.games mark clipped top and
272
+ bottom · a mark loses its tips in the feed · cover vs contain for a persona icon
273
+ CONTAIN, NOT COVER — THE SLOT IS FORGIVING, THE ASSET NEED NOT BE PERFECT. `cover` fills the box
274
+ by scaling to the LARGER ratio and cropping the overflow, split evenly between the two edges. For
275
+ a square drawing in a square slot the two are the same declaration, which is why this stood for
276
+ twenty-six personas without a complaint: every fleet icon.svg is square. pin.games' mark is not —
277
+ its box is 700.19 x 767.26, so `cover` scaled it by width, overflowed the height by 9.6%, and took
278
+ 4.8% off the top and 4.8% off the bottom. What lives at the top and bottom of that drawing is the
279
+ blue bracket and the red one, which is exactly the pair the founder reported as cut. On a 76px
280
+ profile avatar that is 3.6px gone from each end.
281
+ Squaring the asset is the other half of this and is being done too, but it is not a substitute:
282
+ an asset can be redrawn wrong tomorrow, while a slot that never crops cannot crop anyone. */
283
+ .pa .mk, .rav .mk { width: 100%; height: 100%; object-fit: contain; display: block; }
272
284
  /* a dark tile must invert in light mode, not stay dark — personas whose avatar ground is a gradient
273
285
  pass avatarBgLight; the rest fall back to their single background. */
274
286
  @media (prefers-color-scheme: light) { :global(html:not([data-theme="dark"])) .pa, :global(html:not([data-theme="dark"])) .rav { background: var(--feed-av-bg-light, var(--feed-av-bg, #fff)); } }
package/src/Profile.astro CHANGED
@@ -127,7 +127,10 @@ const vars = [
127
127
  @media (prefers-reduced-motion: reduce) { .avatar { transition: none; } }
128
128
  /* the avatar content is slotted (persona-owned) → style it globally */
129
129
  .avatar :global(svg) { width: 100%; height: 100%; display: block; }
130
- .avatar :global(img) { width: 100%; height: 100%; display: block; object-fit: cover; }
130
+ /* contain, not cover — see the note on .pa .mk in Posts.astro. A square icon renders identically
131
+ under either; a mark whose box is taller than wide loses its top and its bottom under `cover`,
132
+ which is what cropped pin.games' blue and red brackets on this very avatar. */
133
+ .avatar :global(img) { width: 100%; height: 100%; display: block; object-fit: contain; }
131
134
  @media (prefers-color-scheme: light) {
132
135
  :global(html:not([data-theme="dark"])) .avatar { border-color: #fff; background: var(--avatar-bg-light, var(--avatar-bg, #fff)); }
133
136
  }