@justai/cuts 0.44.0 → 0.45.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/Sheet.astro +49 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justai/cuts",
3
- "version": "0.44.0",
3
+ "version": "0.45.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": {
package/src/Sheet.astro CHANGED
@@ -111,6 +111,18 @@ const doneLabel = done === false ? null : (done ?? (detent === "large" ? "Done"
111
111
  </dialog>
112
112
 
113
113
  <style>
114
+ /* queries: the page scrolls behind an open sheet · background scroll lock · the sheet feels
115
+ inactive · scroll behind the dialog
116
+ THE PAGE BEHIND MUST NOT MOVE. `showModal()` makes the rest of the document INERT — it cannot
117
+ be clicked or focused — but it does not stop it SCROLLING, and a person swiping over the dim
118
+ watches the page slide under a sheet they thought they were using. The founder named the
119
+ feeling exactly: "aktif sheet sanki aktif degilmis hissi veriyor". Measured before fixing:
120
+ scrollY went 2244 → 1644 with a sheet open.
121
+
122
+ Set by script on <html> and reference-counted, because sheets nest: unlocking on the first
123
+ close would free the page while a second sheet is still up. */
124
+ :global(html[data-sheet-locked]) { overflow: hidden; }
125
+
114
126
  /* Trailing and level with the title, where a navigation bar's right-hand action lives — the one
115
127
  place a thumb looks for "I am finished here". */
116
128
  .sheet-done {
@@ -227,6 +239,18 @@ const doneLabel = done === false ? null : (done ?? (detent === "large" ? "Done"
227
239
  flex: 1 1 auto;
228
240
  min-height: 0;
229
241
  overflow-y: auto;
242
+ /* queries: the sheet scrolls sideways · horizontal scroll in a sheet · long text pushes the
243
+ panel wider · content wider than the sheet
244
+ NOTHING SCROLLS SIDEWAYS. A sheet is a column with a known width, and a person dragging it
245
+ left to read the end of a mime type has been handed a defect rather than a feature —
246
+ measured at 867px of content in a 390px panel, from one unbroken string.
247
+
248
+ Two halves, and both are needed: `overflow-x` stops the panel itself from becoming a
249
+ horizontal scroller, and `overflow-wrap` makes a long unbroken token WRAP instead of being
250
+ silently clipped by it. Content that is genuinely wide — a table, a code block — carries its
251
+ own scroller; the sheet never becomes one. */
252
+ overflow-x: hidden;
253
+ overflow-wrap: anywhere;
230
254
  /* A scroll that reaches its end must not become the page's scroll behind the sheet. */
231
255
  overscroll-behavior: contain;
232
256
  -webkit-overflow-scrolling: touch;
@@ -260,9 +284,34 @@ const doneLabel = done === false ? null : (done ?? (detent === "large" ? "Done"
260
284
  const FLICK = 0.55; // px/ms — a fast flick dismisses regardless of distance
261
285
  const wired = new WeakSet<HTMLDialogElement>();
262
286
 
287
+ // queries: background scroll lock · html data-sheet-locked · nested sheets unlock too early ·
288
+ // how does the sheet know it opened
289
+ //
290
+ // COUNTED, NOT TOGGLED. Sheets nest — qr's share sheet opens the file door on top of itself —
291
+ // so unlocking on the first `close` would free the page while a second sheet is still up. The
292
+ // count goes 0→1 to lock and 1→0 to unlock, and nothing in between touches it.
293
+ //
294
+ // WATCHED WITH A MutationObserver, because `<dialog>` has no "opened" event and consumers call
295
+ // `showModal()` themselves (qr does, from its own script). Hooking only the delegated opener
296
+ // would lock for sheets opened one way and not the other — which is worse than not locking at
297
+ // all, because it would work in testing and fail in the persona.
298
+ let openCount = 0;
299
+ const relock = (delta: number) => {
300
+ openCount = Math.max(0, openCount + delta);
301
+ if (openCount > 0) document.documentElement.setAttribute("data-sheet-locked", "");
302
+ else document.documentElement.removeAttribute("data-sheet-locked");
303
+ };
304
+
263
305
  function wire(sheet: HTMLDialogElement) {
264
306
  if (wired.has(sheet) || !sheet.showModal) return;
265
307
  wired.add(sheet);
308
+ let wasOpen = sheet.open;
309
+ if (wasOpen) relock(1);
310
+ new MutationObserver(() => {
311
+ if (sheet.open === wasOpen) return;
312
+ wasOpen = sheet.open;
313
+ relock(wasOpen ? 1 : -1);
314
+ }).observe(sheet, { attributes: true, attributeFilter: ["open"] });
266
315
  const panel = sheet.querySelector<HTMLElement>("[data-panel]");
267
316
  const grip = sheet.querySelector<HTMLElement>("[data-grip]");
268
317
  if (!panel) return;