cursedbelt 5.89.0 → 5.91.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 (73) hide show
  1. package/dist/react/LazyToaster.d.ts +3 -0
  2. package/dist/react/LazyToaster.d.ts.map +1 -0
  3. package/dist/react/LazyToaster.js +21 -0
  4. package/dist/react/LazyToaster.js.map +1 -0
  5. package/dist/react/components/DataTable/DataTable.d.ts +14 -1
  6. package/dist/react/components/DataTable/DataTable.d.ts.map +1 -1
  7. package/dist/react/components/DataTable/DataTable.js +18 -7
  8. package/dist/react/components/DataTable/DataTable.js.map +1 -1
  9. package/dist/react/components/Drawer.d.ts +8 -1
  10. package/dist/react/components/Drawer.d.ts.map +1 -1
  11. package/dist/react/components/Drawer.js +2 -2
  12. package/dist/react/components/Drawer.js.map +1 -1
  13. package/dist/react/components/Page.d.ts +9 -1
  14. package/dist/react/components/Page.d.ts.map +1 -1
  15. package/dist/react/components/Page.js +3 -2
  16. package/dist/react/components/Page.js.map +1 -1
  17. package/dist/react/createApiHooks.d.ts +30 -0
  18. package/dist/react/createApiHooks.d.ts.map +1 -1
  19. package/dist/react/createApiHooks.js +31 -2
  20. package/dist/react/createApiHooks.js.map +1 -1
  21. package/dist/react/file-tree/FileTree.d.ts.map +1 -1
  22. package/dist/react/file-tree/FileTree.js +9 -3
  23. package/dist/react/file-tree/FileTree.js.map +1 -1
  24. package/dist/react/index.d.ts +2 -0
  25. package/dist/react/index.d.ts.map +1 -1
  26. package/dist/react/index.js +2 -0
  27. package/dist/react/index.js.map +1 -1
  28. package/dist/react/nav/RailPlaces.d.ts +6 -2
  29. package/dist/react/nav/RailPlaces.d.ts.map +1 -1
  30. package/dist/react/nav/RailPlaces.js +1 -1
  31. package/dist/react/nav/RailPlaces.js.map +1 -1
  32. package/dist/react/optimistic/index.d.ts +10 -0
  33. package/dist/react/optimistic/index.d.ts.map +1 -0
  34. package/dist/react/optimistic/index.js +22 -0
  35. package/dist/react/optimistic/index.js.map +1 -0
  36. package/dist/react/optimistic/optimistic.d.ts +159 -0
  37. package/dist/react/optimistic/optimistic.d.ts.map +1 -0
  38. package/dist/react/optimistic/optimistic.js +216 -0
  39. package/dist/react/optimistic/optimistic.js.map +1 -0
  40. package/dist/react/optimistic/revert.d.ts +47 -0
  41. package/dist/react/optimistic/revert.d.ts.map +1 -0
  42. package/dist/react/optimistic/revert.js +235 -0
  43. package/dist/react/optimistic/revert.js.map +1 -0
  44. package/dist/styles-areas/analytics.css +5 -5
  45. package/dist/styles-areas/data-table.css +5 -5
  46. package/dist/styles-utilities.css +10 -0
  47. package/package.json +1 -1
  48. package/scripts/styleAreas.ts +3 -0
  49. package/src/react/LazyToaster.spec.tsx +18 -0
  50. package/src/react/LazyToaster.tsx +26 -0
  51. package/src/react/components/DataTable/DataTable.tsx +37 -7
  52. package/src/react/components/DataTable/columnHeaders.spec.tsx +83 -0
  53. package/src/react/components/Drawer.tsx +13 -0
  54. package/src/react/components/Modal.spec.tsx +14 -0
  55. package/src/react/components/Page.spec.tsx +20 -0
  56. package/src/react/components/Page.tsx +35 -14
  57. package/src/react/createApiHooks.spec.tsx +77 -0
  58. package/src/react/createApiHooks.ts +58 -1
  59. package/src/react/file-tree/FileTree.spec.tsx +14 -0
  60. package/src/react/file-tree/FileTree.tsx +9 -3
  61. package/src/react/index.ts +2 -0
  62. package/src/react/nav/RailPlaces.spec.tsx +6 -0
  63. package/src/react/nav/RailPlaces.tsx +12 -2
  64. package/src/react/optimistic/index.ts +36 -0
  65. package/src/react/optimistic/optimistic.spec.tsx +225 -0
  66. package/src/react/optimistic/optimistic.ts +326 -0
  67. package/src/react/optimistic/queryTarget.spec.ts +64 -0
  68. package/src/react/optimistic/revert.spec.ts +114 -0
  69. package/src/react/optimistic/revert.ts +238 -0
  70. package/src/react/optimistic/staysOffTheEntry.spec.ts +38 -0
  71. package/src/styles-areas/analytics.css +5 -5
  72. package/src/styles-areas/data-table.css +5 -5
  73. package/src/styles-utilities.css +10 -0
@@ -0,0 +1,216 @@
1
+ /**
2
+ * `optimistic()` — do what the user asked NOW, tell the server after, and take it back only if
3
+ * the server says no.
4
+ *
5
+ * The owner, 2026-10-22 (forge task 3583): *"I want the cursedforge apps to use optimistic
6
+ * updates, where the UI doesn't wait for the server before doing what the user wants … if the
7
+ * delete fails it can popup a toast error but it would already be on the next track either way."*
8
+ *
9
+ * optimistic({
10
+ * label: `delete “${track.title}”`,
11
+ * targets: [
12
+ * { set: setTracks, apply: (rows) => rows.filter((r) => r.id !== track.id) },
13
+ * { set: setTotal, apply: (n) => n - 1, revert: counterRevert },
14
+ * ],
15
+ * request: () => api.deleteTracks({ ids: [track.id] }),
16
+ * });
17
+ *
18
+ * What it does, in order:
19
+ *
20
+ * 1. **Applies every target's forward edit at once**, through a setState-shaped `set` — React's
21
+ * own setter, a store's, or {@link queryTarget} for a TanStack cache. It records the state
22
+ * each edit started from and produced, inside the updater, so the rollback is derived
23
+ * (`revert.ts`) and no caller writes an undo by hand.
24
+ * 2. **Sends the request** — after any earlier request with the same `key` has settled, so two
25
+ * quick edits to one record reach the server in the order they were made. The UI never waits
26
+ * for that queue; only the network does.
27
+ * 3. **On failure**, reverts exactly what this change touched (concurrent changes survive) and
28
+ * shows an error toast naming what did not happen, with **Retry** (OVL-9) — Retry applies the
29
+ * change again and resends it.
30
+ * 4. **While anything is in flight**, the page asks before it is closed or reloaded, because the
31
+ * user has already seen the change happen and would otherwise lose it without knowing.
32
+ *
33
+ * Side effects that must NOT be undone stay outside it — the user's own words: a deleted song
34
+ * that comes back after a failed delete comes back to the list, but the player stays on the next
35
+ * track. Call the player before `optimistic()`, not as a target.
36
+ *
37
+ * It never rejects: the resolved {@link OptimisticOutcome} says what happened, so a fire-and-forget
38
+ * call (`void optimistic(...)`) cannot leak an unhandled rejection.
39
+ */
40
+ import { defaultIdOf, inverseOf } from './revert.js';
41
+ /** Pair a setter with its edit so the two are type-checked against each other. */
42
+ export const target = (t) => t;
43
+ let pending = [];
44
+ let nextId = 1;
45
+ const listeners = new Set();
46
+ const queues = new Map();
47
+ const emit = () => {
48
+ for (const listener of listeners)
49
+ listener();
50
+ };
51
+ /** Every optimistic change whose request has not settled, oldest first. */
52
+ export const pendingChanges = () => pending;
53
+ /** Subscribe to {@link pendingChanges} — the `useSyncExternalStore` shape. */
54
+ export const subscribePendingChanges = (listener) => {
55
+ listeners.add(listener);
56
+ return () => {
57
+ listeners.delete(listener);
58
+ };
59
+ };
60
+ /**
61
+ * Resolves once nothing is pending — the moment to re-read a list that is not a TanStack query,
62
+ * so a read that started before the last change landed cannot draw it as undone.
63
+ */
64
+ export const whenSettled = () => pending.length === 0
65
+ ? Promise.resolve()
66
+ : new Promise((resolve) => {
67
+ const off = subscribePendingChanges(() => {
68
+ if (pending.length > 0)
69
+ return;
70
+ off();
71
+ resolve();
72
+ });
73
+ });
74
+ /**
75
+ * Closing the tab while a change is unsent loses it, and the user has already seen it happen.
76
+ * The listener exists only while something is pending, so an idle page leaves bfcache alone.
77
+ */
78
+ const onBeforeUnload = (event) => {
79
+ event.preventDefault();
80
+ // Chrome still wants returnValue set; the text itself is never shown.
81
+ event.returnValue = '';
82
+ };
83
+ const track = (change) => {
84
+ const wasIdle = pending.length === 0;
85
+ pending = [...pending, change];
86
+ if (wasIdle && typeof window !== 'undefined')
87
+ window.addEventListener('beforeunload', onBeforeUnload);
88
+ emit();
89
+ return () => {
90
+ pending = pending.filter((p) => p.id !== change.id);
91
+ if (pending.length === 0 && typeof window !== 'undefined') {
92
+ window.removeEventListener('beforeunload', onBeforeUnload);
93
+ }
94
+ emit();
95
+ };
96
+ };
97
+ /** Run `task` after every earlier task under `key` has settled. */
98
+ const enqueue = (key, task) => {
99
+ if (key === undefined)
100
+ return task();
101
+ const run = (queues.get(key) ?? Promise.resolve()).then(task, task);
102
+ const tail = run.catch(() => undefined);
103
+ queues.set(key, tail);
104
+ void tail.then(() => {
105
+ if (queues.get(key) === tail)
106
+ queues.delete(key);
107
+ });
108
+ return run;
109
+ };
110
+ const isStep = (t) => typeof t.now === 'function';
111
+ function applyStep(step) {
112
+ step.now();
113
+ return { rollback: step.undo, settled: () => undefined };
114
+ }
115
+ function applyTarget(t) {
116
+ // Captured INSIDE the updater: React runs updaters during render, not when `set` is called.
117
+ // The rollback updater is queued behind this one, so by the time it runs the capture is there.
118
+ let seen;
119
+ const scope = t.scope?.();
120
+ t.begin?.();
121
+ t.set((current) => {
122
+ const after = t.apply(current);
123
+ seen = { before: current, after };
124
+ return after;
125
+ });
126
+ return {
127
+ rollback: () => {
128
+ if (t.scope && !Object.is(t.scope(), scope))
129
+ return; // another view is on screen now
130
+ t.set((current) => {
131
+ if (!seen)
132
+ return current; // the edit never ran (its owner unmounted) — nothing to undo
133
+ const { before, after } = seen;
134
+ return t.revert ? t.revert(current, before, after) : inverseOf(before, after, t.idOf ?? defaultIdOf)(current);
135
+ });
136
+ },
137
+ settled: () => t.settled?.(),
138
+ };
139
+ }
140
+ const messageOf = (error) => {
141
+ if (error instanceof Error)
142
+ return error.message || undefined;
143
+ if (typeof error === 'string')
144
+ return error || undefined;
145
+ return undefined;
146
+ };
147
+ /** Apply now, send, revert on failure. See the file header. */
148
+ export function optimistic(options) {
149
+ const { label, request, key, onSuccess, onError } = options;
150
+ const targets = options.targets === undefined ? [] : Array.isArray(options.targets) ? options.targets : [options.targets];
151
+ const applied = targets.map((t) => isStep(t) ? applyStep(t) : applyTarget(t));
152
+ const done = track({ id: nextId++, label, key, startedAt: Date.now() });
153
+ return enqueue(key, request).then((value) => {
154
+ done();
155
+ for (const a of applied)
156
+ a.settled();
157
+ onSuccess?.(value);
158
+ return { ok: true, value };
159
+ }, async (error) => {
160
+ done();
161
+ // Newest-first, so a target listed twice unwinds in the order it was wound.
162
+ for (const a of [...applied].reverse())
163
+ a.rollback();
164
+ for (const a of applied)
165
+ a.settled();
166
+ onError?.(error);
167
+ if (options.toast !== false) {
168
+ // Loaded at the first refusal, never before: a static import would put sonner (~10 kB
169
+ // gzip) back on every app's entry chunk, which cursedbelt 5.64 took it off (forge task
170
+ // 2900) — flix's and roms' entries grew by it the first time they imported this.
171
+ const { toast } = await import('../Toast.js');
172
+ toast.error(`Couldn’t ${label}`, {
173
+ id: `optimistic:${key ?? label}`,
174
+ description: options.describeError ? options.describeError(error) : messageOf(error),
175
+ action: { label: 'Retry', onClick: () => void optimistic(options) },
176
+ });
177
+ }
178
+ return { ok: false, error };
179
+ });
180
+ }
181
+ /** Changes in flight per cache entry, so the entry is refetched once, after the LAST of them. */
182
+ const inFlight = new Map();
183
+ /**
184
+ * A TanStack cache entry as a target's `set`: cancels the key's in-flight fetch first (so a
185
+ * response already on the wire cannot land on top of the change), writes through `setQueryData`,
186
+ * and refetches the key once the request settles, so the cache ends on the server's truth.
187
+ * An entry with no data yet is left alone.
188
+ *
189
+ * 🔴 The refetch waits for the LAST change in flight on the entry. Refetching after each one
190
+ * (the first version) meant two quick deletes drew the second row back for a moment: the first
191
+ * answer's refetch read the server before the second delete had landed (collections and desk
192
+ * both met it, forge task 3583; TanStack's own docs gate on `isMutating`).
193
+ *
194
+ * targets: [{ ...queryTarget(queryClient, ['lists']), apply: (lists) => lists.filter(...) }]
195
+ */
196
+ export function queryTarget(client, queryKey, { refetch = true } = {}) {
197
+ const entry = JSON.stringify(queryKey);
198
+ return {
199
+ set: (updater) => {
200
+ void client.cancelQueries({ queryKey, exact: true });
201
+ client.setQueryData(queryKey, (old) => (old === undefined ? old : updater(old)));
202
+ },
203
+ begin: () => inFlight.set(entry, (inFlight.get(entry) ?? 0) + 1),
204
+ settled: () => {
205
+ const left = (inFlight.get(entry) ?? 1) - 1;
206
+ if (left > 0) {
207
+ inFlight.set(entry, left);
208
+ return;
209
+ }
210
+ inFlight.delete(entry);
211
+ if (refetch)
212
+ void client.invalidateQueries({ queryKey });
213
+ },
214
+ };
215
+ }
216
+ //# sourceMappingURL=optimistic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optimistic.js","sourceRoot":"","sources":["../../../src/react/optimistic/optimistic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The rollback half of an optimistic change, DERIVED from the change itself.
3
+ *
4
+ * A caller writes only the forward edit (`rows => rows.filter(r => r.id !== id)`); this works out
5
+ * what that edit touched by comparing the state before and after it, and returns an inverse that
6
+ * undoes exactly that — applied to whatever the state is when the request fails, not to a
7
+ * snapshot. That difference is the whole reason this file exists:
8
+ *
9
+ * delete A, delete B, A fails → restoring the snapshot taken before A would bring back B too
10
+ * (or, taken before B, drop a rename made since). The inverse instead puts A back where it
11
+ * was and leaves B gone and the rename standing.
12
+ *
13
+ * The rules, by shape (recursing through plain objects and arrays, so a patch three levels down
14
+ * reverts three levels down):
15
+ *
16
+ * | shape | the change | its inverse, against the current state |
17
+ * |---|---|---|
18
+ * | array of records (`id`) or primitives | item removed | put back after its nearest surviving neighbor, unless it is back already |
19
+ * | | item added | taken out by id |
20
+ * | | item changed | recurse into it by id (only fields still holding the optimistic value revert) |
21
+ * | | reordered | the old order back, only if the current order is still the optimistic one |
22
+ * | plain object | key changed / added / removed | per key, recursing |
23
+ * | `Set` / `Map` | member added / removed | the member out / back |
24
+ * | anything else | replaced | the old value back only if the current one is still the optimistic one |
25
+ *
26
+ * "Only if it still holds the optimistic value" is what lets two edits race: rename to "B", then
27
+ * to "C" — if the first request fails, the title is "C" by now, so it stays "C".
28
+ *
29
+ * Returns the current state UNCHANGED (same reference) when nothing needs reverting, so a React
30
+ * setter given the inverse does not re-render.
31
+ */
32
+ /** How an array item is identified. Records by `id` (or `key`), primitives by value. */
33
+ export type IdOf = (item: unknown) => unknown;
34
+ export declare const defaultIdOf: IdOf;
35
+ /**
36
+ * The inverse of the change `before → after`, as a function of the state at revert time.
37
+ * `idOf` names array items; the default reads `id`, then `key`, and uses a primitive's value.
38
+ */
39
+ export declare function inverseOf<S>(before: S, after: S, idOf?: IdOf): (current: S) => S;
40
+ /**
41
+ * A counter's inverse: give back the difference the change made, whatever has happened since.
42
+ * The whole-value rule is wrong for a count — two deletes take a total 10 → 9 → 8, and when the
43
+ * first fails the total is 8, not 9, so "revert only if still optimistic" would never fire. Pass
44
+ * it as a target's `revert`: `{ set: setTotal, apply: (n) => n - 1, revert: counterRevert }`.
45
+ */
46
+ export declare const counterRevert: (current: number, before: number, after: number) => number;
47
+ //# sourceMappingURL=revert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revert.d.ts","sourceRoot":"","sources":["../../../src/react/optimistic/revert.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,wFAAwF;AACxF,MAAM,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;AAI9C,eAAO,MAAM,WAAW,EAAE,IAMzB,CAAC;AAQF;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,GAAE,IAAkB,GAAG,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAE7F;AA6KD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,EAAE,QAAQ,MAAM,EAAE,OAAO,MAAM,KAAG,MACnD,CAAC"}
@@ -0,0 +1,235 @@
1
+ /**
2
+ * The rollback half of an optimistic change, DERIVED from the change itself.
3
+ *
4
+ * A caller writes only the forward edit (`rows => rows.filter(r => r.id !== id)`); this works out
5
+ * what that edit touched by comparing the state before and after it, and returns an inverse that
6
+ * undoes exactly that — applied to whatever the state is when the request fails, not to a
7
+ * snapshot. That difference is the whole reason this file exists:
8
+ *
9
+ * delete A, delete B, A fails → restoring the snapshot taken before A would bring back B too
10
+ * (or, taken before B, drop a rename made since). The inverse instead puts A back where it
11
+ * was and leaves B gone and the rename standing.
12
+ *
13
+ * The rules, by shape (recursing through plain objects and arrays, so a patch three levels down
14
+ * reverts three levels down):
15
+ *
16
+ * | shape | the change | its inverse, against the current state |
17
+ * |---|---|---|
18
+ * | array of records (`id`) or primitives | item removed | put back after its nearest surviving neighbor, unless it is back already |
19
+ * | | item added | taken out by id |
20
+ * | | item changed | recurse into it by id (only fields still holding the optimistic value revert) |
21
+ * | | reordered | the old order back, only if the current order is still the optimistic one |
22
+ * | plain object | key changed / added / removed | per key, recursing |
23
+ * | `Set` / `Map` | member added / removed | the member out / back |
24
+ * | anything else | replaced | the old value back only if the current one is still the optimistic one |
25
+ *
26
+ * "Only if it still holds the optimistic value" is what lets two edits race: rename to "B", then
27
+ * to "C" — if the first request fails, the title is "C" by now, so it stays "C".
28
+ *
29
+ * Returns the current state UNCHANGED (same reference) when nothing needs reverting, so a React
30
+ * setter given the inverse does not re-render.
31
+ */
32
+ const NO_ID = Symbol('no-id');
33
+ export const defaultIdOf = (item) => {
34
+ if (item === null || typeof item !== 'object')
35
+ return item;
36
+ const record = item;
37
+ if (record.id !== undefined)
38
+ return record.id;
39
+ if (record.key !== undefined)
40
+ return record.key;
41
+ return NO_ID;
42
+ };
43
+ const isPlainObject = (value) => {
44
+ if (value === null || typeof value !== 'object')
45
+ return false;
46
+ const proto = Object.getPrototypeOf(value);
47
+ return proto === Object.prototype || proto === null;
48
+ };
49
+ /**
50
+ * The inverse of the change `before → after`, as a function of the state at revert time.
51
+ * `idOf` names array items; the default reads `id`, then `key`, and uses a primitive's value.
52
+ */
53
+ export function inverseOf(before, after, idOf = defaultIdOf) {
54
+ return (current) => revertValue(before, after, current, idOf);
55
+ }
56
+ function revertValue(before, after, current, idOf) {
57
+ if (Object.is(before, after))
58
+ return current;
59
+ if (Array.isArray(before) && Array.isArray(after) && Array.isArray(current)) {
60
+ const reverted = revertArray(before, after, current, idOf);
61
+ if (reverted !== undefined)
62
+ return reverted;
63
+ }
64
+ if (before instanceof Set && after instanceof Set && current instanceof Set) {
65
+ return revertSet(before, after, current);
66
+ }
67
+ if (before instanceof Map && after instanceof Map && current instanceof Map) {
68
+ return revertMap(before, after, current, idOf);
69
+ }
70
+ if (isPlainObject(before) && isPlainObject(after) && isPlainObject(current)) {
71
+ return revertObject(before, after, current, idOf);
72
+ }
73
+ return Object.is(current, after) ? before : current;
74
+ }
75
+ function revertObject(before, after, current, idOf) {
76
+ let out;
77
+ const write = (key, value, remove = false) => {
78
+ out ??= { ...current };
79
+ if (remove)
80
+ delete out[key];
81
+ else
82
+ out[key] = value;
83
+ };
84
+ for (const key of new Set([...Object.keys(before), ...Object.keys(after)])) {
85
+ const had = Object.hasOwn(before, key);
86
+ const has = Object.hasOwn(after, key);
87
+ if (had && has) {
88
+ if (Object.is(before[key], after[key]))
89
+ continue;
90
+ if (!Object.hasOwn(current, key))
91
+ continue; // removed since — not ours to bring back
92
+ const next = revertValue(before[key], after[key], current[key], idOf);
93
+ if (!Object.is(next, current[key]))
94
+ write(key, next);
95
+ }
96
+ else if (has) {
97
+ // Added by the change: take it out if it is still what the change put there.
98
+ if (Object.hasOwn(current, key) && Object.is(current[key], after[key]))
99
+ write(key, undefined, true);
100
+ }
101
+ else if (!Object.hasOwn(current, key)) {
102
+ write(key, before[key]); // removed by the change, and still gone
103
+ }
104
+ }
105
+ return out ?? current;
106
+ }
107
+ /** `undefined` when the items cannot be identified — the caller falls back to whole-value. */
108
+ function revertArray(before, after, current, idOf) {
109
+ const ids = (list) => list.map(idOf);
110
+ const beforeIds = ids(before);
111
+ const afterIds = ids(after);
112
+ const unique = (list) => !list.includes(NO_ID) && new Set(list).size === list.length;
113
+ if (!unique(beforeIds) || !unique(afterIds))
114
+ return undefined;
115
+ const beforeById = new Map(beforeIds.map((id, i) => [id, before[i]]));
116
+ const afterById = new Map(afterIds.map((id, i) => [id, after[i]]));
117
+ let out = current;
118
+ let touched = false;
119
+ const edit = () => {
120
+ if (!touched) {
121
+ out = [...current];
122
+ touched = true;
123
+ }
124
+ };
125
+ // 1 · Items the change ADDED go, if they are still there.
126
+ const added = afterIds.filter((id) => !beforeById.has(id));
127
+ if (added.length > 0) {
128
+ const drop = new Set(added);
129
+ const kept = out.filter((item) => !drop.has(idOf(item)));
130
+ if (kept.length !== out.length) {
131
+ out = kept;
132
+ touched = true;
133
+ }
134
+ }
135
+ // 2 · Items the change CHANGED revert field by field.
136
+ for (const [id, was] of beforeById) {
137
+ const now = afterById.get(id);
138
+ if (now === undefined || Object.is(was, now))
139
+ continue;
140
+ const at = out.findIndex((item) => Object.is(idOf(item), id));
141
+ if (at < 0)
142
+ continue;
143
+ const next = revertValue(was, now, out[at], idOf);
144
+ if (!Object.is(next, out[at])) {
145
+ edit();
146
+ out[at] = next;
147
+ }
148
+ }
149
+ // 3 · Items the change REMOVED come back beside the neighbor they had.
150
+ const removed = beforeIds.flatMap((id, index) => (afterById.has(id) ? [] : [{ id, index }]));
151
+ for (const { id, index } of removed) {
152
+ if (out.some((item) => Object.is(idOf(item), id)))
153
+ continue;
154
+ edit();
155
+ let at = 0;
156
+ for (let i = index - 1; i >= 0; i--) {
157
+ const neighbor = out.findIndex((item) => Object.is(idOf(item), beforeIds[i]));
158
+ if (neighbor >= 0) {
159
+ at = neighbor + 1;
160
+ break;
161
+ }
162
+ }
163
+ out.splice(at, 0, beforeById.get(id));
164
+ }
165
+ // 4 · A REORDER: the old order back, only where the optimistic order still stands.
166
+ const kept = beforeIds.filter((id) => afterById.has(id));
167
+ const keptAfter = afterIds.filter((id) => beforeById.has(id));
168
+ if (kept.some((id, i) => !Object.is(id, keptAfter[i]))) {
169
+ const keep = new Set(kept);
170
+ const positions = [];
171
+ out.forEach((item, i) => {
172
+ if (keep.has(idOf(item)))
173
+ positions.push(i);
174
+ });
175
+ const nowOrder = positions.map((i) => idOf(out[i]));
176
+ if (nowOrder.length === keptAfter.length && nowOrder.every((id, i) => Object.is(id, keptAfter[i]))) {
177
+ const byId = new Map(positions.map((i) => [idOf(out[i]), out[i]]));
178
+ edit();
179
+ positions.forEach((slot, i) => {
180
+ out[slot] = byId.get(kept[i]);
181
+ });
182
+ }
183
+ }
184
+ return touched ? out : current;
185
+ }
186
+ function revertSet(before, after, current) {
187
+ let out;
188
+ const copy = () => {
189
+ out ??= new Set(current);
190
+ return out;
191
+ };
192
+ for (const member of after) {
193
+ if (!before.has(member) && current.has(member))
194
+ copy().delete(member);
195
+ }
196
+ for (const member of before) {
197
+ if (!after.has(member) && !current.has(member))
198
+ copy().add(member);
199
+ }
200
+ return out ?? current;
201
+ }
202
+ function revertMap(before, after, current, idOf) {
203
+ let out;
204
+ const copy = () => {
205
+ out ??= new Map(current);
206
+ return out;
207
+ };
208
+ for (const key of new Set([...before.keys(), ...after.keys()])) {
209
+ const had = before.has(key);
210
+ const has = after.has(key);
211
+ if (had && has) {
212
+ if (Object.is(before.get(key), after.get(key)) || !current.has(key))
213
+ continue;
214
+ const next = revertValue(before.get(key), after.get(key), current.get(key), idOf);
215
+ if (!Object.is(next, current.get(key)))
216
+ copy().set(key, next);
217
+ }
218
+ else if (has) {
219
+ if (current.has(key) && Object.is(current.get(key), after.get(key)))
220
+ copy().delete(key);
221
+ }
222
+ else if (!current.has(key)) {
223
+ copy().set(key, before.get(key));
224
+ }
225
+ }
226
+ return out ?? current;
227
+ }
228
+ /**
229
+ * A counter's inverse: give back the difference the change made, whatever has happened since.
230
+ * The whole-value rule is wrong for a count — two deletes take a total 10 → 9 → 8, and when the
231
+ * first fails the total is 8, not 9, so "revert only if still optimistic" would never fire. Pass
232
+ * it as a target's `revert`: `{ set: setTotal, apply: (n) => n - 1, revert: counterRevert }`.
233
+ */
234
+ export const counterRevert = (current, before, after) => current + (before - after);
235
+ //# sourceMappingURL=revert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revert.js","sourceRoot":"","sources":["../../../src/react/optimistic/revert.ts"],"names":[],"mappings":"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"}
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 156 candidate(s).
21
+ * 159 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -33,19 +33,19 @@
33
33
  @source inline("@max-6xl:text-[length:0] @max-6xl:w-0! @max-xl:contents @xl:basis-auto @xl:block @xl:hidden");
34
34
  @source inline("[&>*]:hidden [&>*]:max-w-none [&>svg]:size-3.5 [&_svg]:size-3 active:cursor-grabbing basis");
35
35
  @source inline("basis-full bg-[var(--cb-frozen-bg)] bg-accent/15 bg-card/95 bg-gradient-to-t bg-muted/30");
36
- @source inline("bg-muted/50 border-accent-solid border-accent/60 border-field-error border-solid border-t-2 col");
37
- @source inline("collapse cursor-grab data-[ending-style]:opacity-0 data-[ending-style]:scale-95");
36
+ @source inline("bg-muted/50 border-accent-solid border-accent/60 border-b-0! border-field-error border-solid");
37
+ @source inline("border-t-2 col collapse cursor-grab data-[ending-style]:opacity-0 data-[ending-style]:scale-95");
38
38
  @source inline("data-[highlighted]:bg-accent/10 data-[highlighted]:text-accent-strong");
39
39
  @source inline("data-[starting-style]:opacity-0 data-[starting-style]:scale-95 disabled:opacity-40 duration-100");
40
40
  @source inline("fade-in focus-visible:ring-field-ring/40 focus-within:border-field-ring focus-within:ring-2");
41
- @source inline("focus-within:ring-field-ring/30 from-card gap-x-6 h-105");
41
+ @source inline("focus-within:ring-field-ring/30 from-card gap-x-6 h-0 h-105");
42
42
  @source inline("has-[[aria-invalid=true]]:border-field-error hover:bg-background hover:bg-muted/40");
43
43
  @source inline("hover:border-field-ring hover:text-warning italic justify-self-end last:border-0 left-11");
44
44
  @source inline("left-2.5 list-none max-h-64 max-h-88 max-sm:flex-1 max-sm:flex-wrap max-sm:min-w-0");
45
45
  @source inline("max-sm:min-w-24 max-sm:min-w-32 max-sm:ml-13.5 max-sm:w-auto max-w-32 max-w-36 max-w-45 max-w-48");
46
46
  @source inline("max-w-60 min-h-20 min-h-dvh min-w-0! min-w-20 min-w-24 min-w-4 min-w-52");
47
47
  @source inline("min-w-[var(--anchor-width)] min-w-max mr-2 mt-1.5 mt-3 mt-4 not-italic ordinal p-0! pb-1.5");
48
- @source inline("pl-1.5 pl-2 pr-0.5 pr-1 pr-10 pr-16 pr-3 pr-9 pt-1 resize-none resize-y right-3");
48
+ @source inline("pl-1.5 pl-2 pr-0.5 pr-1 pr-10 pr-16 pr-3 pr-9 pt-1 py-0! resize-none resize-y right-3");
49
49
  @source inline("ring-field-error/30 self self-stretch shadow-[1px_0_0_0_var(--color-border)] shrink-[2] size-7");
50
50
  @source inline("slide-in-from-top-1 table-fixed text-[length:0] text-accent-solid-foreground");
51
51
  @source inline("text-field-placeholder text-success text-xl to-transparent top-3 transition-[transform,opacity]");
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 151 candidate(s).
21
+ * 154 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -33,19 +33,19 @@
33
33
  @source inline("@max-6xl:text-[length:0] @max-6xl:w-0! @max-xl:contents @xl:basis-auto @xl:block @xl:hidden");
34
34
  @source inline("[&>*]:hidden [&>*]:max-w-none [&>svg]:size-3.5 [&_svg]:size-3 active:cursor-grabbing basis-full");
35
35
  @source inline("bg-[var(--cb-frozen-bg)] bg-accent/15 bg-card/95 bg-gradient-to-t bg-muted/30 bg-muted/50");
36
- @source inline("border-accent-solid border-accent/60 border-field-error border-solid border-t-2 col collapse");
37
- @source inline("cursor-grab data-[ending-style]:opacity-0 data-[ending-style]:scale-95");
36
+ @source inline("border-accent-solid border-accent/60 border-b-0! border-field-error border-solid border-t-2 col");
37
+ @source inline("collapse cursor-grab data-[ending-style]:opacity-0 data-[ending-style]:scale-95");
38
38
  @source inline("data-[highlighted]:bg-accent/10 data-[highlighted]:text-accent-strong");
39
39
  @source inline("data-[starting-style]:opacity-0 data-[starting-style]:scale-95 disabled:opacity-40 duration-100");
40
40
  @source inline("fade-in focus-visible:ring-field-ring/40 focus-within:border-field-ring focus-within:ring-2");
41
- @source inline("focus-within:ring-field-ring/30 from-card gap-x-6 h-105");
41
+ @source inline("focus-within:ring-field-ring/30 from-card gap-x-6 h-0 h-105");
42
42
  @source inline("has-[[aria-invalid=true]]:border-field-error hover:bg-background hover:bg-muted/40");
43
43
  @source inline("hover:border-field-ring hover:text-warning justify-self-end last:border-0 left-11 left-2.5");
44
44
  @source inline("list-none max-h-64 max-h-88 max-sm:flex-1 max-sm:flex-wrap max-sm:min-w-0 max-sm:min-w-24");
45
45
  @source inline("max-sm:min-w-32 max-sm:ml-13.5 max-sm:w-auto max-w-32 max-w-36 max-w-45 max-w-48 max-w-60");
46
46
  @source inline("min-h-20 min-h-dvh min-w-0! min-w-24 min-w-4 min-w-52 min-w-[var(--anchor-width)] min-w-max mr-2");
47
47
  @source inline("mt-1.5 mt-3 mt-4 not-italic ordinal p-0! pb-1.5 pl-1.5 pl-2 pr-0.5 pr-1 pr-10 pr-16 pr-3 pr-9");
48
- @source inline("pt-1 resize-none resize-y right-3 ring-field-error/30 self self-stretch");
48
+ @source inline("pt-1 py-0! resize-none resize-y right-3 ring-field-error/30 self self-stretch");
49
49
  @source inline("shadow-[1px_0_0_0_var(--color-border)] size-7 slide-in-from-top-1 table-fixed text-[length:0]");
50
50
  @source inline("text-accent-solid-foreground text-field-placeholder text-success text-xl to-transparent top-3");
51
51
  @source inline("transition-[transform,opacity] w-0! w-11 w-12 w-20 w-32 w-36 w-40 w-88");
@@ -656,6 +656,9 @@
656
656
  width: 100%;
657
657
  height: 100%;
658
658
  }
659
+ .h-0 {
660
+ height: 0px;
661
+ }
659
662
  .h-0\.5 {
660
663
  height: calc(var(--spacing) * 0.5);
661
664
  }
@@ -1726,6 +1729,10 @@
1726
1729
  border-bottom-style: var(--tw-border-style);
1727
1730
  border-bottom-width: 0px;
1728
1731
  }
1732
+ .border-b-0\! {
1733
+ border-bottom-style: var(--tw-border-style) !important;
1734
+ border-bottom-width: 0px !important;
1735
+ }
1729
1736
  .border-b-2 {
1730
1737
  border-bottom-style: var(--tw-border-style);
1731
1738
  border-bottom-width: 2px;
@@ -2639,6 +2646,9 @@
2639
2646
  .py-0 {
2640
2647
  padding-block: 0px;
2641
2648
  }
2649
+ .py-0\! {
2650
+ padding-block: 0px !important;
2651
+ }
2642
2652
  .py-0\.5 {
2643
2653
  padding-block: calc(var(--spacing) * 0.5);
2644
2654
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursedbelt",
3
- "version": "5.89.0",
3
+ "version": "5.91.0",
4
4
  "license": "ISC",
5
5
  "type": "module",
6
6
  "description": "The React design system of the cursedbelt split — components, styles, theme. cursedbelt-core below it; server tier in cursedbelt-server.",