react-native-webrtc-kaleidoscope 2.2.2 → 2.3.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/README.md +30 -0
- package/dist/src/components/preset-control-panel/preset-control-panel.d.ts +9 -1
- package/dist/src/components/preset-control-panel/preset-control-panel.d.ts.map +1 -1
- package/dist/src/components/preset-control-panel/preset-control-panel.js +7 -3
- package/dist/src/components/preset-control-panel/preset-control-panel.js.map +1 -1
- package/dist/src/kaleidoscope/controls.d.ts.map +1 -1
- package/dist/src/kaleidoscope/controls.js +6 -3
- package/dist/src/kaleidoscope/controls.js.map +1 -1
- package/dist/src/kaleidoscope/shader-to-spec.d.ts +15 -1
- package/dist/src/kaleidoscope/shader-to-spec.d.ts.map +1 -1
- package/dist/src/kaleidoscope/shader-to-spec.js +23 -4
- package/dist/src/kaleidoscope/shader-to-spec.js.map +1 -1
- package/dist/src/kaleidoscope/types.d.ts +3 -1
- package/dist/src/kaleidoscope/types.d.ts.map +1 -1
- package/dist/src/kaleidoscope/types.js.map +1 -1
- package/dist/src/persistence/async-storage-store.d.ts +3 -0
- package/dist/src/persistence/async-storage-store.d.ts.map +1 -0
- package/dist/src/persistence/async-storage-store.js +26 -0
- package/dist/src/persistence/async-storage-store.js.map +1 -0
- package/dist/src/persistence/index.d.ts +4 -0
- package/dist/src/persistence/index.d.ts.map +1 -0
- package/dist/src/persistence/index.js +22 -0
- package/dist/src/persistence/index.js.map +1 -0
- package/dist/src/persistence/provider.d.ts +38 -0
- package/dist/src/persistence/provider.d.ts.map +1 -0
- package/dist/src/persistence/provider.js +96 -0
- package/dist/src/persistence/provider.js.map +1 -0
- package/dist/src/persistence/state.d.ts +54 -0
- package/dist/src/persistence/state.d.ts.map +1 -0
- package/dist/src/persistence/state.js +126 -0
- package/dist/src/persistence/state.js.map +1 -0
- package/package.json +14 -2
- package/src/components/preset-control-panel/preset-control-panel.tsx +15 -2
- package/src/kaleidoscope/controls.ts +6 -3
- package/src/kaleidoscope/shader-to-spec.ts +32 -5
- package/src/kaleidoscope/types.ts +3 -1
- package/src/persistence/async-storage-store.ts +33 -0
- package/src/persistence/index.ts +28 -0
- package/src/persistence/provider.tsx +165 -0
- package/src/persistence/state.ts +167 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// Persistence: the pure state module. The stored shape, its key, the tolerant
|
|
2
|
+
// parse, and the pure helpers the provider uses to prune, merge, and project
|
|
3
|
+
// stored state. No React, no storage; the store interface is the only boundary
|
|
4
|
+
// (a consumer may back it with AsyncStorage, localStorage, MMKV, anything
|
|
5
|
+
// promise-shaped). Everything here is unit-testable in plain Node.
|
|
6
|
+
//
|
|
7
|
+
// What persists is the person's selection: the preset id they last applied, the
|
|
8
|
+
// per-layer uniform patches they dialed in through the control panels (keyed by
|
|
9
|
+
// preset, so tweaks to several presets all survive), and the shared mask edge.
|
|
10
|
+
|
|
11
|
+
import type { MaskInput } from '../kaleidoscope/types';
|
|
12
|
+
import type { KaleidoscopePresetBook } from '../kaleidoscope.preset-book.types';
|
|
13
|
+
|
|
14
|
+
/** One layer's stored uniform overrides (the wire shape `onPatch` emits). */
|
|
15
|
+
export type StoredLayerUniforms = Readonly<Record<string, number | readonly number[]>>;
|
|
16
|
+
|
|
17
|
+
/** A preset's stored overrides, keyed by layer id. */
|
|
18
|
+
export type StoredPatchMap = Readonly<Record<string, StoredLayerUniforms>>;
|
|
19
|
+
|
|
20
|
+
/** Every preset's stored overrides, keyed by preset id. */
|
|
21
|
+
export type StoredPatches = Readonly<Record<string, StoredPatchMap>>;
|
|
22
|
+
|
|
23
|
+
/** The single live-patch shape, identical to `KaleidoscopeControls['onPatch']`'s argument. */
|
|
24
|
+
export type StoredPatch = {
|
|
25
|
+
readonly id: string;
|
|
26
|
+
readonly uniforms: StoredLayerUniforms;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type StoredKaleidoscopeState = {
|
|
30
|
+
readonly version: 1;
|
|
31
|
+
/** The last-applied preset id, or null when nothing was selected. */
|
|
32
|
+
readonly presetId: string | null;
|
|
33
|
+
/** The shared segmentation edge. */
|
|
34
|
+
readonly mask: MaskInput;
|
|
35
|
+
/** Per-preset, per-layer uniform overrides from the control panels. */
|
|
36
|
+
readonly patches: StoredPatches;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The backing store. `load` resolves null when nothing (or nothing readable) is
|
|
41
|
+
* stored; `save` swallows its own failures (persistence is a convenience, never
|
|
42
|
+
* a crash).
|
|
43
|
+
*/
|
|
44
|
+
export interface KaleidoscopeStateStore {
|
|
45
|
+
load(): Promise<StoredKaleidoscopeState | null>;
|
|
46
|
+
save(state: StoredKaleidoscopeState): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const KALEIDOSCOPE_STATE_KEY = 'kaleidoscope.state.v1';
|
|
50
|
+
|
|
51
|
+
export const DEFAULT_MASK: MaskInput = { hardness: 0.5, threshold: 0.5 };
|
|
52
|
+
|
|
53
|
+
export const serializeKaleidoscopeState = (state: StoredKaleidoscopeState): string =>
|
|
54
|
+
JSON.stringify(state);
|
|
55
|
+
|
|
56
|
+
const clamp01 = (value: number): number => Math.min(1, Math.max(0, value));
|
|
57
|
+
|
|
58
|
+
const isUniformValue = (value: unknown): value is number | readonly number[] =>
|
|
59
|
+
typeof value === 'number' ||
|
|
60
|
+
(Array.isArray(value) && value.every((entry) => typeof entry === 'number'));
|
|
61
|
+
|
|
62
|
+
// A patch map (layer id -> uniforms) with every malformed entry dropped.
|
|
63
|
+
const parsePatchMap = (raw: unknown): StoredPatchMap | null => {
|
|
64
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return null;
|
|
65
|
+
const out: Record<string, StoredLayerUniforms> = {};
|
|
66
|
+
for (const [layerId, uniforms] of Object.entries(raw)) {
|
|
67
|
+
if (typeof uniforms !== 'object' || uniforms === null || Array.isArray(uniforms)) continue;
|
|
68
|
+
const kept: Record<string, number | readonly number[]> = {};
|
|
69
|
+
for (const [key, value] of Object.entries(uniforms)) {
|
|
70
|
+
if (isUniformValue(value)) kept[key] = value;
|
|
71
|
+
}
|
|
72
|
+
if (Object.keys(kept).length > 0) out[layerId] = kept;
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Tolerant parse: any malformed or wrong-version payload reads as null, and a
|
|
79
|
+
* malformed `patches` subtree degrades to the valid subset rather than killing
|
|
80
|
+
* the whole state. Mask values clamp to 0..1 (the verbs' documented range).
|
|
81
|
+
*/
|
|
82
|
+
export const parseStoredKaleidoscopeState = (
|
|
83
|
+
raw: string | null,
|
|
84
|
+
): StoredKaleidoscopeState | null => {
|
|
85
|
+
if (!raw) return null;
|
|
86
|
+
let parsed: unknown;
|
|
87
|
+
try {
|
|
88
|
+
parsed = JSON.parse(raw);
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
if (typeof parsed !== 'object' || parsed === null) return null;
|
|
93
|
+
const candidate = parsed as Record<string, unknown>;
|
|
94
|
+
if (candidate.version !== 1) return null;
|
|
95
|
+
if (candidate.presetId !== null && typeof candidate.presetId !== 'string') return null;
|
|
96
|
+
const mask = candidate.mask as Record<string, unknown> | null | undefined;
|
|
97
|
+
if (!mask || typeof mask.hardness !== 'number' || typeof mask.threshold !== 'number') {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const patches: Record<string, StoredPatchMap> = {};
|
|
101
|
+
if (typeof candidate.patches === 'object' && candidate.patches !== null) {
|
|
102
|
+
for (const [presetId, rawMap] of Object.entries(candidate.patches)) {
|
|
103
|
+
const map = parsePatchMap(rawMap);
|
|
104
|
+
if (map && Object.keys(map).length > 0) patches[presetId] = map;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
version: 1,
|
|
109
|
+
presetId: candidate.presetId as string | null,
|
|
110
|
+
mask: { hardness: clamp01(mask.hardness), threshold: clamp01(mask.threshold) },
|
|
111
|
+
patches,
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// The layer ids a preset can be patched on: the layers that carry uniforms
|
|
116
|
+
// (`image` and `direct` layers have none and cannot be patched).
|
|
117
|
+
const tunableLayerIds = (book: KaleidoscopePresetBook, presetId: string): ReadonlySet<string> => {
|
|
118
|
+
const preset = book[presetId];
|
|
119
|
+
if (!preset) return new Set();
|
|
120
|
+
return new Set(preset.layers.filter((layer) => 'uniforms' in layer).map((layer) => layer.id));
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Reconcile stored state against the consumer's current book: a preset that no
|
|
125
|
+
* longer exists reads as "none", a patch for a vanished preset or layer is
|
|
126
|
+
* dropped. Stale state degrades silently; it never crashes the picker.
|
|
127
|
+
*/
|
|
128
|
+
export const pruneStoredState = (
|
|
129
|
+
state: StoredKaleidoscopeState,
|
|
130
|
+
book: KaleidoscopePresetBook,
|
|
131
|
+
): StoredKaleidoscopeState => {
|
|
132
|
+
const presetId = state.presetId !== null && state.presetId in book ? state.presetId : null;
|
|
133
|
+
const patches: Record<string, StoredPatchMap> = {};
|
|
134
|
+
for (const [id, map] of Object.entries(state.patches)) {
|
|
135
|
+
if (!(id in book)) continue;
|
|
136
|
+
const tunable = tunableLayerIds(book, id);
|
|
137
|
+
const kept: Record<string, StoredLayerUniforms> = {};
|
|
138
|
+
for (const [layerId, uniforms] of Object.entries(map)) {
|
|
139
|
+
if (tunable.has(layerId)) kept[layerId] = uniforms;
|
|
140
|
+
}
|
|
141
|
+
if (Object.keys(kept).length > 0) patches[id] = kept;
|
|
142
|
+
}
|
|
143
|
+
return { version: 1, presetId, mask: state.mask, patches };
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/** Merge one live patch into a preset's stored overrides (immutably). */
|
|
147
|
+
export const mergePatch = (
|
|
148
|
+
patches: StoredPatches,
|
|
149
|
+
presetId: string,
|
|
150
|
+
patch: StoredPatch,
|
|
151
|
+
): StoredPatches => ({
|
|
152
|
+
...patches,
|
|
153
|
+
[presetId]: {
|
|
154
|
+
...patches[presetId],
|
|
155
|
+
[patch.id]: { ...patches[presetId]?.[patch.id], ...patch.uniforms },
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Project a preset's stored overrides into the array shape the `kaleidoscope`
|
|
161
|
+
* verb takes as `patches`.
|
|
162
|
+
*/
|
|
163
|
+
export const patchListFor = (
|
|
164
|
+
patches: StoredPatches,
|
|
165
|
+
presetId: string,
|
|
166
|
+
): ReadonlyArray<StoredPatch> =>
|
|
167
|
+
Object.entries(patches[presetId] ?? {}).map(([id, uniforms]) => ({ id, uniforms }));
|