@adea-ai/ui 0.36.0 → 0.37.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.
|
@@ -19,6 +19,21 @@ export type SurfacePreference = 'opaque' | 'frosted' | 'translucent';
|
|
|
19
19
|
export declare const APPEARANCE_STORAGE_KEY = "appearance";
|
|
20
20
|
/** The pre-#425 key. Migration reads it and never deletes it. */
|
|
21
21
|
export declare const LEGACY_THEME_STORAGE_KEY = "theme";
|
|
22
|
+
/**
|
|
23
|
+
* Recovery envelope for unread preference documents (Dev Runtime spec,
|
|
24
|
+
* "Compatibility, migrations, and waivers": a failed migration retains the
|
|
25
|
+
* original record; it never silently rewrites or deletes the input). A
|
|
26
|
+
* malformed or future-version document is quarantined here — before any
|
|
27
|
+
* write can touch the main key — so saving valid preferences later can never
|
|
28
|
+
* destroy the user's unread data.
|
|
29
|
+
*/
|
|
30
|
+
export declare const APPEARANCE_RECOVERY_STORAGE_KEY = "appearance.recovery";
|
|
31
|
+
export type AppearanceRecoveryEnvelope = Readonly<{
|
|
32
|
+
schemaVersion: 1;
|
|
33
|
+
reason: 'corrupt_json' | 'unsupported_record';
|
|
34
|
+
capturedAt: string;
|
|
35
|
+
raw: string;
|
|
36
|
+
}>;
|
|
22
37
|
export declare const DARK_QUERY = "(prefers-color-scheme: dark)";
|
|
23
38
|
export declare const REDUCED_TRANSPARENCY_QUERY = "(prefers-reduced-transparency: reduce)";
|
|
24
39
|
export declare const defaultAppearancePreferences: AppearancePreferencesV2;
|
|
@@ -197,8 +212,9 @@ export declare function migrateLegacyThemeValue(stored: string | null): Appearan
|
|
|
197
212
|
type AppearanceStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
|
|
198
213
|
/**
|
|
199
214
|
* Read the appearance preferences: the v2 key first, then the legacy `theme`
|
|
200
|
-
* key, then defaults.
|
|
201
|
-
*
|
|
215
|
+
* key, then defaults. A malformed or future-version document is quarantined
|
|
216
|
+
* into the recovery envelope and the defaults are returned; storage failures
|
|
217
|
+
* degrade to defaults like every other blocked-storage consumer.
|
|
202
218
|
*/
|
|
203
219
|
export declare function readAppearancePreferences(storage: AppearanceStorage | undefined): AppearancePreferencesV2;
|
|
204
220
|
export declare function writeAppearancePreferences(storage: AppearanceStorage | undefined, preferences: AppearancePreferencesV2): void;
|
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
var APPEARANCE_STORAGE_KEY = "appearance";
|
|
3
3
|
/** The pre-#425 key. Migration reads it and never deletes it. */
|
|
4
4
|
var LEGACY_THEME_STORAGE_KEY = "theme";
|
|
5
|
+
/**
|
|
6
|
+
* Recovery envelope for unread preference documents (Dev Runtime spec,
|
|
7
|
+
* "Compatibility, migrations, and waivers": a failed migration retains the
|
|
8
|
+
* original record; it never silently rewrites or deletes the input). A
|
|
9
|
+
* malformed or future-version document is quarantined here — before any
|
|
10
|
+
* write can touch the main key — so saving valid preferences later can never
|
|
11
|
+
* destroy the user's unread data.
|
|
12
|
+
*/
|
|
13
|
+
var APPEARANCE_RECOVERY_STORAGE_KEY = "appearance.recovery";
|
|
5
14
|
var DARK_QUERY = "(prefers-color-scheme: dark)";
|
|
6
15
|
var REDUCED_TRANSPARENCY_QUERY = "(prefers-reduced-transparency: reduce)";
|
|
7
16
|
var defaultAppearancePreferences = Object.freeze({
|
|
@@ -599,9 +608,27 @@ function migrateLegacyThemeValue(stored) {
|
|
|
599
608
|
};
|
|
600
609
|
}
|
|
601
610
|
/**
|
|
611
|
+
* Quarantine an unread raw document into the recovery envelope. Runs at read
|
|
612
|
+
* time — before any later write can overwrite the main key — and is
|
|
613
|
+
* idempotent: re-reading the same unread value refreshes the capture without
|
|
614
|
+
* losing it.
|
|
615
|
+
*/
|
|
616
|
+
function retainRecoveryEnvelope(storage, raw, reason) {
|
|
617
|
+
try {
|
|
618
|
+
const envelope = {
|
|
619
|
+
schemaVersion: 1,
|
|
620
|
+
reason,
|
|
621
|
+
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
622
|
+
raw
|
|
623
|
+
};
|
|
624
|
+
storage.setItem(APPEARANCE_RECOVERY_STORAGE_KEY, JSON.stringify(envelope));
|
|
625
|
+
} catch {}
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
602
628
|
* Read the appearance preferences: the v2 key first, then the legacy `theme`
|
|
603
|
-
* key, then defaults.
|
|
604
|
-
*
|
|
629
|
+
* key, then defaults. A malformed or future-version document is quarantined
|
|
630
|
+
* into the recovery envelope and the defaults are returned; storage failures
|
|
631
|
+
* degrade to defaults like every other blocked-storage consumer.
|
|
605
632
|
*/
|
|
606
633
|
function readAppearancePreferences(storage) {
|
|
607
634
|
if (!storage) return defaultAppearancePreferences;
|
|
@@ -612,9 +639,12 @@ function readAppearancePreferences(storage) {
|
|
|
612
639
|
try {
|
|
613
640
|
parsed = JSON.parse(raw);
|
|
614
641
|
} catch {
|
|
642
|
+
retainRecoveryEnvelope(storage, raw, "corrupt_json");
|
|
615
643
|
return defaultAppearancePreferences;
|
|
616
644
|
}
|
|
617
|
-
|
|
645
|
+
const normalized = normalizeAppearancePreferences(parsed);
|
|
646
|
+
if (normalized.retainedRaw !== void 0) retainRecoveryEnvelope(storage, raw, typeof parsed === "object" && parsed !== null ? "unsupported_record" : "corrupt_json");
|
|
647
|
+
return normalized.value;
|
|
618
648
|
}
|
|
619
649
|
return migrateLegacyThemeValue(storage.getItem("theme")) ?? defaultAppearancePreferences;
|
|
620
650
|
} catch {
|
|
@@ -765,4 +795,4 @@ function kebabCase(value) {
|
|
|
765
795
|
return value.replaceAll(/[A-Z]/g, (character) => `-${character.toLocaleLowerCase()}`);
|
|
766
796
|
}
|
|
767
797
|
//#endregion
|
|
768
|
-
export { APPEARANCE_STORAGE_KEY, ColorParseError, DARK_QUERY, LEGACY_THEME_STORAGE_KEY, REDUCED_TRANSPARENCY_QUERY, accentPresetById, accentPresets, appearanceThemeScript, applyAppearanceToDocument, builtinThemeRegistry, colorToHex, contrastRatio, defaultAppearancePreferences, deriveAccentRoles, flatVariantTokens, migrateLegacyThemeValue, normalizeAccentValue, normalizeAppearancePreferences, parseColor, readAppearancePreferences, resolveAppearanceMode, resolveAppearanceState, resolveSurface, resolveThemeVariant, themeSelectionVariantId, validateThemeRegistry, writeAppearancePreferences };
|
|
798
|
+
export { APPEARANCE_RECOVERY_STORAGE_KEY, APPEARANCE_STORAGE_KEY, ColorParseError, DARK_QUERY, LEGACY_THEME_STORAGE_KEY, REDUCED_TRANSPARENCY_QUERY, accentPresetById, accentPresets, appearanceThemeScript, applyAppearanceToDocument, builtinThemeRegistry, colorToHex, contrastRatio, defaultAppearancePreferences, deriveAccentRoles, flatVariantTokens, migrateLegacyThemeValue, normalizeAccentValue, normalizeAppearancePreferences, parseColor, readAppearancePreferences, resolveAppearanceMode, resolveAppearanceState, resolveSurface, resolveThemeVariant, themeSelectionVariantId, validateThemeRegistry, writeAppearancePreferences };
|
package/package.json
CHANGED
|
@@ -41,6 +41,22 @@ export type SurfacePreference = 'opaque' | 'frosted' | 'translucent'
|
|
|
41
41
|
export const APPEARANCE_STORAGE_KEY = 'appearance'
|
|
42
42
|
/** The pre-#425 key. Migration reads it and never deletes it. */
|
|
43
43
|
export const LEGACY_THEME_STORAGE_KEY = 'theme'
|
|
44
|
+
/**
|
|
45
|
+
* Recovery envelope for unread preference documents (Dev Runtime spec,
|
|
46
|
+
* "Compatibility, migrations, and waivers": a failed migration retains the
|
|
47
|
+
* original record; it never silently rewrites or deletes the input). A
|
|
48
|
+
* malformed or future-version document is quarantined here — before any
|
|
49
|
+
* write can touch the main key — so saving valid preferences later can never
|
|
50
|
+
* destroy the user's unread data.
|
|
51
|
+
*/
|
|
52
|
+
export const APPEARANCE_RECOVERY_STORAGE_KEY = 'appearance.recovery'
|
|
53
|
+
|
|
54
|
+
export type AppearanceRecoveryEnvelope = Readonly<{
|
|
55
|
+
schemaVersion: 1
|
|
56
|
+
reason: 'corrupt_json' | 'unsupported_record'
|
|
57
|
+
capturedAt: string
|
|
58
|
+
raw: string
|
|
59
|
+
}>
|
|
44
60
|
|
|
45
61
|
export const DARK_QUERY = '(prefers-color-scheme: dark)'
|
|
46
62
|
export const REDUCED_TRANSPARENCY_QUERY = '(prefers-reduced-transparency: reduce)'
|
|
@@ -837,10 +853,35 @@ export function migrateLegacyThemeValue(
|
|
|
837
853
|
|
|
838
854
|
type AppearanceStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>
|
|
839
855
|
|
|
856
|
+
/**
|
|
857
|
+
* Quarantine an unread raw document into the recovery envelope. Runs at read
|
|
858
|
+
* time — before any later write can overwrite the main key — and is
|
|
859
|
+
* idempotent: re-reading the same unread value refreshes the capture without
|
|
860
|
+
* losing it.
|
|
861
|
+
*/
|
|
862
|
+
function retainRecoveryEnvelope(
|
|
863
|
+
storage: AppearanceStorage,
|
|
864
|
+
raw: string,
|
|
865
|
+
reason: AppearanceRecoveryEnvelope['reason']
|
|
866
|
+
): void {
|
|
867
|
+
try {
|
|
868
|
+
const envelope: AppearanceRecoveryEnvelope = {
|
|
869
|
+
schemaVersion: 1,
|
|
870
|
+
reason,
|
|
871
|
+
capturedAt: new Date().toISOString(),
|
|
872
|
+
raw,
|
|
873
|
+
}
|
|
874
|
+
storage.setItem(APPEARANCE_RECOVERY_STORAGE_KEY, JSON.stringify(envelope))
|
|
875
|
+
} catch {
|
|
876
|
+
// Quarantine is best-effort; the active preference still fails closed.
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
840
880
|
/**
|
|
841
881
|
* Read the appearance preferences: the v2 key first, then the legacy `theme`
|
|
842
|
-
* key, then defaults.
|
|
843
|
-
*
|
|
882
|
+
* key, then defaults. A malformed or future-version document is quarantined
|
|
883
|
+
* into the recovery envelope and the defaults are returned; storage failures
|
|
884
|
+
* degrade to defaults like every other blocked-storage consumer.
|
|
844
885
|
*/
|
|
845
886
|
export function readAppearancePreferences(
|
|
846
887
|
storage: AppearanceStorage | undefined
|
|
@@ -853,9 +894,18 @@ export function readAppearancePreferences(
|
|
|
853
894
|
try {
|
|
854
895
|
parsed = JSON.parse(raw)
|
|
855
896
|
} catch {
|
|
897
|
+
retainRecoveryEnvelope(storage, raw, 'corrupt_json')
|
|
856
898
|
return defaultAppearancePreferences
|
|
857
899
|
}
|
|
858
|
-
|
|
900
|
+
const normalized = normalizeAppearancePreferences(parsed)
|
|
901
|
+
if (normalized.retainedRaw !== undefined) {
|
|
902
|
+
retainRecoveryEnvelope(
|
|
903
|
+
storage,
|
|
904
|
+
raw,
|
|
905
|
+
typeof parsed === 'object' && parsed !== null ? 'unsupported_record' : 'corrupt_json'
|
|
906
|
+
)
|
|
907
|
+
}
|
|
908
|
+
return normalized.value
|
|
859
909
|
}
|
|
860
910
|
return (
|
|
861
911
|
migrateLegacyThemeValue(storage.getItem(LEGACY_THEME_STORAGE_KEY)) ??
|
package/src/styles/dev-view.css
CHANGED
|
@@ -791,3 +791,98 @@
|
|
|
791
791
|
transition: none;
|
|
792
792
|
}
|
|
793
793
|
}
|
|
794
|
+
|
|
795
|
+
/* Session deep-link recovery: a visible, announced banner above the body. */
|
|
796
|
+
.dev-recovery-banner {
|
|
797
|
+
margin: 0;
|
|
798
|
+
padding: 0.5rem 1.25rem;
|
|
799
|
+
border-block-end: 1px solid var(--dev-border);
|
|
800
|
+
color: var(--dev-muted);
|
|
801
|
+
font-size: 0.8125rem;
|
|
802
|
+
background: var(--dev-surface-muted, transparent);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/* Provider-backed archive shelf content. */
|
|
806
|
+
.dev-archive-shelf__content {
|
|
807
|
+
max-block-size: 14rem;
|
|
808
|
+
overflow-y: auto;
|
|
809
|
+
border-block-start: 1px solid var(--dev-border);
|
|
810
|
+
padding-block-end: 0.5rem;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
.dev-archive-shelf__list {
|
|
814
|
+
margin: 0;
|
|
815
|
+
padding: 0;
|
|
816
|
+
list-style: none;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
.dev-archive-shelf__item {
|
|
820
|
+
display: flex;
|
|
821
|
+
align-items: center;
|
|
822
|
+
gap: 0.5rem;
|
|
823
|
+
padding: 0.35rem 0.75rem;
|
|
824
|
+
font-size: 0.8125rem;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
.dev-archive-shelf__item .dev-tree-row__title {
|
|
828
|
+
min-width: 0;
|
|
829
|
+
flex: 1;
|
|
830
|
+
overflow: hidden;
|
|
831
|
+
text-overflow: ellipsis;
|
|
832
|
+
white-space: nowrap;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
.dev-archive-shelf__actions {
|
|
836
|
+
display: flex;
|
|
837
|
+
align-items: center;
|
|
838
|
+
gap: 0.25rem;
|
|
839
|
+
flex: none;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
.dev-archive-shelf__confirm {
|
|
843
|
+
display: flex;
|
|
844
|
+
align-items: center;
|
|
845
|
+
gap: 0.375rem;
|
|
846
|
+
font-size: 0.75rem;
|
|
847
|
+
color: var(--dev-muted);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
.dev-archive-action {
|
|
851
|
+
border: 1px solid var(--dev-border);
|
|
852
|
+
border-radius: 0.375rem;
|
|
853
|
+
background: transparent;
|
|
854
|
+
color: inherit;
|
|
855
|
+
padding: 0.125rem 0.5rem;
|
|
856
|
+
font-size: 0.75rem;
|
|
857
|
+
cursor: pointer;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
.dev-archive-action:hover {
|
|
861
|
+
background: color-mix(in srgb, var(--foreground) 8%, transparent);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
.dev-archive-action--destructive {
|
|
865
|
+
color: var(--destructive);
|
|
866
|
+
border-color: color-mix(in srgb, var(--destructive) 40%, transparent);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
.dev-archive-shelf__handoff {
|
|
870
|
+
margin: 0;
|
|
871
|
+
padding: 0.5rem 0.75rem;
|
|
872
|
+
font-size: 0.75rem;
|
|
873
|
+
line-height: 1.45;
|
|
874
|
+
color: var(--dev-muted);
|
|
875
|
+
border-block-start: 1px solid var(--dev-border);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/* Truthful per-pane provider states inside the utility slots. */
|
|
879
|
+
.dev-pane-state {
|
|
880
|
+
padding: 0.75rem 0.875rem;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
.dev-pane-state__line {
|
|
884
|
+
margin: 0;
|
|
885
|
+
font-size: 0.8125rem;
|
|
886
|
+
line-height: 1.5;
|
|
887
|
+
color: var(--dev-muted);
|
|
888
|
+
}
|