@justai/cuts 0.43.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.
- package/package.json +3 -2
- package/src/FilePicker.astro +13 -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.
|
|
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": {
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"./ground.css": "./src/ground.css",
|
|
25
25
|
"./FilePicker.astro": "./src/FilePicker.astro",
|
|
26
26
|
"./ActionList.astro": "./src/ActionList.astro",
|
|
27
|
-
"./ActionRow.astro": "./src/ActionRow.astro"
|
|
27
|
+
"./ActionRow.astro": "./src/ActionRow.astro",
|
|
28
|
+
"./Files.astro": "./src/FilePicker.astro"
|
|
28
29
|
},
|
|
29
30
|
"peerDependencies": {
|
|
30
31
|
"astro": ">=5"
|
package/src/FilePicker.astro
CHANGED
|
@@ -49,8 +49,19 @@ import Sheet from "./Sheet.astro";
|
|
|
49
49
|
*
|
|
50
50
|
* One element rather than two cuts, because it is one room: two frames would be two documents,
|
|
51
51
|
* two atrium routers and two copies of the same kernel open at once, to ask the same file system
|
|
52
|
-
* a question in each direction.
|
|
53
|
-
*
|
|
52
|
+
* a question in each direction.
|
|
53
|
+
*
|
|
54
|
+
* ── THE NAME (settled 2026-07-27) ────────────────────────────────────────────────────────────
|
|
55
|
+
*
|
|
56
|
+
* It was called `FilePicker` when picking was all it did, and by the time it could pick, save and
|
|
57
|
+
* read, the name described a third of it. A name that has to be explained is a name that is wrong:
|
|
58
|
+
* a builder reading "picker" in the catalogue has no reason to look here for saving.
|
|
59
|
+
*
|
|
60
|
+
* So it is exported as **`Files.astro`** — the persona's door to its files, which is what it is.
|
|
61
|
+
* `FilePicker.astro` still resolves to the same component and always will: eleven personas import
|
|
62
|
+
* it under that name, and breaking them to correct a word would be charging the fleet for the
|
|
63
|
+
* ecosystem's own late naming. An alias costs one line in `exports`; a rename costs eleven repos,
|
|
64
|
+
* eleven deploys, and one of them forgotten. New consumers import `Files`.
|
|
54
65
|
*
|
|
55
66
|
* A method on the element rather than a global, so two pickers on one page are two pickers and
|
|
56
67
|
* nothing has to be coordinated through a name only one of them can own.
|
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;
|