@proveanything/smartlinks 2.1.8 → 2.1.10
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/dist/docs/API_SUMMARY.md +1 -1
- package/dist/docs/theme-tokens.md +8 -0
- package/dist/idbConnection.d.ts +14 -0
- package/dist/idbConnection.js +91 -0
- package/dist/persistentCache.js +36 -95
- package/dist/theme.css +20 -0
- package/dist/translationCache.js +49 -114
- package/docs/API_SUMMARY.md +1 -1
- package/docs/theme-tokens.md +8 -0
- package/package.json +87 -87
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -99,6 +99,14 @@ renders correctly even against a host that sets none of them.
|
|
|
99
99
|
|
|
100
100
|
Dark mode is a *theme* (a different value-set), not a separate token set — the host swaps the values.
|
|
101
101
|
|
|
102
|
+
- **Host:** in dark mode, send a dark value-set — in the iframe address (`#slt=` with `theme: "dark"`, and the
|
|
103
|
+
legacy `theme=` payload with `m: "d"`) or on the mount root. Sending only the hook (`dark=1`, `.dark`)
|
|
104
|
+
leaves the app to guess.
|
|
105
|
+
- **App:** `theme.css` has dark fallbacks under `.dark` / `[data-theme="dark"]`, so an app built on it stays
|
|
106
|
+
readable even when a host sends the hook and no values. An app with its own CSS variables should do the
|
|
107
|
+
same: a light fallback at the root, a dark one under the hook — never a light colour that also stands
|
|
108
|
+
in for dark.
|
|
109
|
+
|
|
102
110
|
---
|
|
103
111
|
|
|
104
112
|
## 3. Theme value-sets (what the AI emits, what the lookbook stores)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface IdbConnection {
|
|
2
|
+
/** The open connection, opening one if needed. */
|
|
3
|
+
open(): Promise<IDBDatabase>;
|
|
4
|
+
/** Forget the current connection so the next call re-opens. */
|
|
5
|
+
drop(closed?: IDBDatabase): void;
|
|
6
|
+
/**
|
|
7
|
+
* Run one transaction against the object store. If the connection is
|
|
8
|
+
* closed, re-open it and try once more. Errors from `body` are not retried.
|
|
9
|
+
*/
|
|
10
|
+
run<T>(mode: IDBTransactionMode, body: (store: IDBObjectStore, tx: IDBTransaction) => T | Promise<T>): Promise<T>;
|
|
11
|
+
}
|
|
12
|
+
/** True for the error a transaction throws on a connection the browser closed. */
|
|
13
|
+
export declare function isClosedConnectionError(error: unknown): boolean;
|
|
14
|
+
export declare function createIdbConnection(dbName: string, storeName: string, version?: number): IdbConnection;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// src/idbConnection.ts
|
|
2
|
+
// Shared IndexedDB connection handling for the SDK's caches.
|
|
3
|
+
//
|
|
4
|
+
// The browser may close an IndexedDB connection behind our back: iOS Safari
|
|
5
|
+
// does it when the tab goes to the background, and any browser does it on
|
|
6
|
+
// storage eviction or a version change. A transaction on a closed connection
|
|
7
|
+
// throws "The database connection is closing", so a cache that keeps one
|
|
8
|
+
// connection for the life of the page silently stops working after that.
|
|
9
|
+
//
|
|
10
|
+
// createIdbConnection() forgets a connection the browser closes and re-opens
|
|
11
|
+
// on the next call; run() retries a transaction once when the connection
|
|
12
|
+
// turns out to be closed.
|
|
13
|
+
/** True for the error a transaction throws on a connection the browser closed. */
|
|
14
|
+
export function isClosedConnectionError(error) {
|
|
15
|
+
const e = error;
|
|
16
|
+
if (!e)
|
|
17
|
+
return false;
|
|
18
|
+
if (e.name === 'InvalidStateError')
|
|
19
|
+
return true;
|
|
20
|
+
const msg = String(e.message || '');
|
|
21
|
+
return /connection is closing|database connection|closed/i.test(msg);
|
|
22
|
+
}
|
|
23
|
+
export function createIdbConnection(dbName, storeName, version = 1) {
|
|
24
|
+
let db = null;
|
|
25
|
+
let opening = null;
|
|
26
|
+
const drop = (closed) => {
|
|
27
|
+
// A newer connection may already have replaced the one that closed.
|
|
28
|
+
if (closed && db && db !== closed)
|
|
29
|
+
return;
|
|
30
|
+
db = null;
|
|
31
|
+
opening = null;
|
|
32
|
+
};
|
|
33
|
+
const open = () => {
|
|
34
|
+
if (db)
|
|
35
|
+
return Promise.resolve(db);
|
|
36
|
+
if (opening)
|
|
37
|
+
return opening;
|
|
38
|
+
opening = new Promise((resolve, reject) => {
|
|
39
|
+
try {
|
|
40
|
+
const req = indexedDB.open(dbName, version);
|
|
41
|
+
req.onupgradeneeded = (event) => {
|
|
42
|
+
const upgrading = event.target.result;
|
|
43
|
+
if (!upgrading.objectStoreNames.contains(storeName)) {
|
|
44
|
+
upgrading.createObjectStore(storeName);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
req.onsuccess = () => {
|
|
48
|
+
const opened = req.result;
|
|
49
|
+
opened.onclose = () => drop(opened);
|
|
50
|
+
opened.onversionchange = () => {
|
|
51
|
+
opened.close();
|
|
52
|
+
drop(opened);
|
|
53
|
+
};
|
|
54
|
+
db = opened;
|
|
55
|
+
resolve(opened);
|
|
56
|
+
};
|
|
57
|
+
req.onerror = () => {
|
|
58
|
+
opening = null;
|
|
59
|
+
reject(req.error);
|
|
60
|
+
};
|
|
61
|
+
req.onblocked = () => {
|
|
62
|
+
opening = null;
|
|
63
|
+
reject(new Error(`[smartlinks] IndexedDB open blocked (${dbName})`));
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
opening = null;
|
|
68
|
+
reject(err);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return opening;
|
|
72
|
+
};
|
|
73
|
+
const run = async (mode, body) => {
|
|
74
|
+
for (let attempt = 0;; attempt++) {
|
|
75
|
+
const current = await open();
|
|
76
|
+
let tx;
|
|
77
|
+
try {
|
|
78
|
+
tx = current.transaction(storeName, mode);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (attempt === 0 && isClosedConnectionError(err)) {
|
|
82
|
+
drop(current);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
return await body(tx.objectStore(storeName), tx);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
return { open, drop, run };
|
|
91
|
+
}
|
package/dist/persistentCache.js
CHANGED
|
@@ -5,10 +5,15 @@
|
|
|
5
5
|
// isIdbAvailable() and wrapped in try/catch so that failures in private-
|
|
6
6
|
// browsing, quota-exceeded situations, or server-side environments are always
|
|
7
7
|
// silent — they never propagate errors to the caller.
|
|
8
|
+
//
|
|
9
|
+
// The connection itself is managed by idbConnection.ts, which re-opens it when
|
|
10
|
+
// the browser closes it behind our back (iOS Safari does this when the tab is
|
|
11
|
+
// backgrounded) instead of leaving the cache dead for the rest of the page.
|
|
12
|
+
import { createIdbConnection } from './idbConnection.js';
|
|
8
13
|
const DB_NAME = 'smartlinks-sdk-cache';
|
|
9
14
|
const STORE_NAME = 'responses';
|
|
10
15
|
const DB_VERSION = 1;
|
|
11
|
-
|
|
16
|
+
const connection = createIdbConnection(DB_NAME, STORE_NAME, DB_VERSION);
|
|
12
17
|
/**
|
|
13
18
|
* Returns true only in environments where IndexedDB is genuinely available.
|
|
14
19
|
* False in Node.js, and in some private-browsing contexts that stub indexedDB
|
|
@@ -22,36 +27,13 @@ export function isIdbAvailable() {
|
|
|
22
27
|
return false;
|
|
23
28
|
}
|
|
24
29
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
req.onupgradeneeded = (event) => {
|
|
32
|
-
const db = event.target.result;
|
|
33
|
-
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
34
|
-
db.createObjectStore(STORE_NAME);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
req.onsuccess = (event) => {
|
|
38
|
-
resolve(event.target.result);
|
|
39
|
-
};
|
|
40
|
-
req.onerror = () => {
|
|
41
|
-
dbPromise = null;
|
|
42
|
-
reject(req.error);
|
|
43
|
-
};
|
|
44
|
-
req.onblocked = () => {
|
|
45
|
-
dbPromise = null;
|
|
46
|
-
reject(new Error('[smartlinks] IndexedDB open blocked'));
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
catch (err) {
|
|
50
|
-
dbPromise = null;
|
|
51
|
-
reject(err);
|
|
52
|
-
}
|
|
30
|
+
/** Resolve when the transaction settles, whatever the outcome. */
|
|
31
|
+
function settled(tx) {
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
tx.oncomplete = () => resolve();
|
|
34
|
+
tx.onerror = () => resolve(); // quota exceeded etc — fail silently
|
|
35
|
+
tx.onabort = () => resolve();
|
|
53
36
|
});
|
|
54
|
-
return dbPromise;
|
|
55
37
|
}
|
|
56
38
|
/**
|
|
57
39
|
* Read an entry from IndexedDB.
|
|
@@ -62,18 +44,11 @@ export async function idbGet(key) {
|
|
|
62
44
|
if (!isIdbAvailable())
|
|
63
45
|
return null;
|
|
64
46
|
try {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
req.onsuccess = () => { var _a; return resolve((_a = req.result) !== null && _a !== void 0 ? _a : null); };
|
|
71
|
-
req.onerror = () => resolve(null);
|
|
72
|
-
}
|
|
73
|
-
catch (_a) {
|
|
74
|
-
resolve(null);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
47
|
+
return await connection.run('readonly', (store) => new Promise((resolve) => {
|
|
48
|
+
const req = store.get(key);
|
|
49
|
+
req.onsuccess = () => { var _a; return resolve((_a = req.result) !== null && _a !== void 0 ? _a : null); };
|
|
50
|
+
req.onerror = () => resolve(null);
|
|
51
|
+
}));
|
|
77
52
|
}
|
|
78
53
|
catch (_a) {
|
|
79
54
|
return null;
|
|
@@ -88,18 +63,9 @@ export async function idbSet(key, entry) {
|
|
|
88
63
|
if (!isIdbAvailable())
|
|
89
64
|
return;
|
|
90
65
|
try {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
95
|
-
tx.objectStore(STORE_NAME).put(entry, key);
|
|
96
|
-
tx.oncomplete = () => resolve();
|
|
97
|
-
tx.onerror = () => resolve(); // quota exceeded etc — fail silently
|
|
98
|
-
tx.onabort = () => resolve();
|
|
99
|
-
}
|
|
100
|
-
catch (_a) {
|
|
101
|
-
resolve();
|
|
102
|
-
}
|
|
66
|
+
await connection.run('readwrite', (store, tx) => {
|
|
67
|
+
store.put(entry, key);
|
|
68
|
+
return settled(tx);
|
|
103
69
|
});
|
|
104
70
|
}
|
|
105
71
|
catch (_a) {
|
|
@@ -114,18 +80,9 @@ export async function idbDelete(key) {
|
|
|
114
80
|
if (!isIdbAvailable())
|
|
115
81
|
return;
|
|
116
82
|
try {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
121
|
-
tx.objectStore(STORE_NAME).delete(key);
|
|
122
|
-
tx.oncomplete = () => resolve();
|
|
123
|
-
tx.onerror = () => resolve();
|
|
124
|
-
tx.onabort = () => resolve();
|
|
125
|
-
}
|
|
126
|
-
catch (_a) {
|
|
127
|
-
resolve();
|
|
128
|
-
}
|
|
83
|
+
await connection.run('readwrite', (store, tx) => {
|
|
84
|
+
store.delete(key);
|
|
85
|
+
return settled(tx);
|
|
129
86
|
});
|
|
130
87
|
}
|
|
131
88
|
catch (_a) { }
|
|
@@ -138,41 +95,25 @@ export async function idbClear(pattern) {
|
|
|
138
95
|
if (!isIdbAvailable())
|
|
139
96
|
return;
|
|
140
97
|
try {
|
|
141
|
-
const db = await openDb();
|
|
142
98
|
if (!pattern) {
|
|
143
|
-
await
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
tx.objectStore(STORE_NAME).clear();
|
|
147
|
-
tx.oncomplete = () => resolve();
|
|
148
|
-
tx.onerror = () => resolve();
|
|
149
|
-
tx.onabort = () => resolve();
|
|
150
|
-
}
|
|
151
|
-
catch (_a) {
|
|
152
|
-
resolve();
|
|
153
|
-
}
|
|
99
|
+
await connection.run('readwrite', (store, tx) => {
|
|
100
|
+
store.clear();
|
|
101
|
+
return settled(tx);
|
|
154
102
|
});
|
|
155
103
|
return;
|
|
156
104
|
}
|
|
157
105
|
// Pattern-based: enumerate all keys and delete matching ones.
|
|
158
|
-
await new Promise((resolve) => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (key.includes(pattern))
|
|
166
|
-
store.delete(key);
|
|
167
|
-
}
|
|
168
|
-
resolve();
|
|
169
|
-
};
|
|
170
|
-
req.onerror = () => resolve();
|
|
171
|
-
}
|
|
172
|
-
catch (_a) {
|
|
106
|
+
await connection.run('readwrite', (store) => new Promise((resolve) => {
|
|
107
|
+
const req = store.getAllKeys();
|
|
108
|
+
req.onsuccess = () => {
|
|
109
|
+
for (const key of req.result) {
|
|
110
|
+
if (key.includes(pattern))
|
|
111
|
+
store.delete(key);
|
|
112
|
+
}
|
|
173
113
|
resolve();
|
|
174
|
-
}
|
|
175
|
-
|
|
114
|
+
};
|
|
115
|
+
req.onerror = () => resolve();
|
|
116
|
+
}));
|
|
176
117
|
}
|
|
177
118
|
catch (_a) { }
|
|
178
119
|
}
|
package/dist/theme.css
CHANGED
|
@@ -41,6 +41,26 @@
|
|
|
41
41
|
--shadow-md: var(--sl-shadow-md, 0 0 #0000);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/* Dark mode without values. A host in dark mode should send a dark value-set (the --sl-* tokens); one
|
|
45
|
+
* that only sets the hook (.dark / data-theme="dark") left every colour on its light fallback — white
|
|
46
|
+
* cards and near-black text on a dark page. Under the hook the fallbacks are dark too; whatever the host
|
|
47
|
+
* does send still wins. (Re-declared on the hook's element, so it works wherever the host puts it.) */
|
|
48
|
+
.dark,
|
|
49
|
+
[data-theme="dark"] {
|
|
50
|
+
--color-background: var(--sl-color-bg, #09090b);
|
|
51
|
+
--color-surface: var(--sl-color-surface, #18181b);
|
|
52
|
+
--color-surface-raised:var(--sl-color-surface-raised, var(--sl-color-surface, #27272a));
|
|
53
|
+
--color-foreground: var(--sl-color-fg, #fafafa);
|
|
54
|
+
--color-muted: var(--sl-color-muted, #a1a1aa);
|
|
55
|
+
--color-border: var(--sl-color-border, #3f3f46);
|
|
56
|
+
--color-primary: var(--sl-color-accent, #6366f1);
|
|
57
|
+
--color-on-primary: var(--sl-color-on-accent, #ffffff);
|
|
58
|
+
--color-primary-soft: var(--sl-color-accent-soft, color-mix(in oklab, var(--sl-color-accent, #6366f1) 20%, transparent));
|
|
59
|
+
--color-success: var(--sl-color-success, #22c55e);
|
|
60
|
+
--color-danger: var(--sl-color-danger, #f87171);
|
|
61
|
+
color-scheme: dark;
|
|
62
|
+
}
|
|
63
|
+
|
|
44
64
|
/* shadcn bridge (optional): if the app uses shadcn's --primary/--background/etc. HSL variables,
|
|
45
65
|
* uncomment to drive them from the SmartLinks tokens too. Left commented so it never overrides an
|
|
46
66
|
* app that has deliberately customised its shadcn palette — opt in per app (see migration step 17).
|
package/dist/translationCache.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { createIdbConnection } from './idbConnection.js';
|
|
1
2
|
const DB_NAME = 'smartlinks-sdk-translations';
|
|
2
3
|
const STORE_NAME = 'translations';
|
|
3
4
|
const DB_VERSION = 1;
|
|
4
5
|
const DEFAULT_LOCAL_TRANSLATION_TTL_MS = 90 * 24 * 60 * 60000;
|
|
5
|
-
|
|
6
|
+
const connection = createIdbConnection(DB_NAME, STORE_NAME, DB_VERSION);
|
|
6
7
|
const memoryTranslationCache = new Map();
|
|
7
8
|
export function getDefaultTranslationCacheTtlMs() {
|
|
8
9
|
return DEFAULT_LOCAL_TRANSLATION_TTL_MS;
|
|
@@ -15,36 +16,13 @@ function isIndexedDbAvailable() {
|
|
|
15
16
|
return false;
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
request.onupgradeneeded = (event) => {
|
|
25
|
-
const db = event.target.result;
|
|
26
|
-
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
27
|
-
db.createObjectStore(STORE_NAME);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
request.onsuccess = (event) => {
|
|
31
|
-
resolve(event.target.result);
|
|
32
|
-
};
|
|
33
|
-
request.onerror = () => {
|
|
34
|
-
dbPromise = null;
|
|
35
|
-
reject(request.error);
|
|
36
|
-
};
|
|
37
|
-
request.onblocked = () => {
|
|
38
|
-
dbPromise = null;
|
|
39
|
-
reject(new Error('[smartlinks] Translation IndexedDB open blocked'));
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
dbPromise = null;
|
|
44
|
-
reject(error);
|
|
45
|
-
}
|
|
19
|
+
/** Resolve when the transaction settles, whatever the outcome. */
|
|
20
|
+
function settled(tx) {
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
tx.oncomplete = () => resolve();
|
|
23
|
+
tx.onerror = () => resolve();
|
|
24
|
+
tx.onabort = () => resolve();
|
|
46
25
|
});
|
|
47
|
-
return dbPromise;
|
|
48
26
|
}
|
|
49
27
|
function stableContextKey(context) {
|
|
50
28
|
if (!context)
|
|
@@ -123,33 +101,26 @@ async function getEntry(key) {
|
|
|
123
101
|
if (!isIndexedDbAvailable())
|
|
124
102
|
return null;
|
|
125
103
|
try {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
};
|
|
147
|
-
request.onerror = () => resolve(null);
|
|
148
|
-
}
|
|
149
|
-
catch (_a) {
|
|
150
|
-
resolve(null);
|
|
151
|
-
}
|
|
152
|
-
});
|
|
104
|
+
return await connection.run('readonly', (store) => new Promise((resolve) => {
|
|
105
|
+
const request = store.get(key);
|
|
106
|
+
request.onsuccess = () => {
|
|
107
|
+
var _a;
|
|
108
|
+
const result = (_a = request.result) !== null && _a !== void 0 ? _a : null;
|
|
109
|
+
if (!result) {
|
|
110
|
+
resolve(null);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (isExpired(result)) {
|
|
114
|
+
memoryTranslationCache.delete(key);
|
|
115
|
+
void deleteEntries([key]);
|
|
116
|
+
resolve(null);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
memoryTranslationCache.set(key, result);
|
|
120
|
+
resolve(result);
|
|
121
|
+
};
|
|
122
|
+
request.onerror = () => resolve(null);
|
|
123
|
+
}));
|
|
153
124
|
}
|
|
154
125
|
catch (_a) {
|
|
155
126
|
return null;
|
|
@@ -170,21 +141,11 @@ async function setEntries(entries) {
|
|
|
170
141
|
if (!isIndexedDbAvailable())
|
|
171
142
|
return;
|
|
172
143
|
try {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
177
|
-
const store = tx.objectStore(STORE_NAME);
|
|
178
|
-
for (const [key, entry] of entries) {
|
|
179
|
-
store.put(entry, key);
|
|
180
|
-
}
|
|
181
|
-
tx.oncomplete = () => resolve();
|
|
182
|
-
tx.onerror = () => resolve();
|
|
183
|
-
tx.onabort = () => resolve();
|
|
184
|
-
}
|
|
185
|
-
catch (_a) {
|
|
186
|
-
resolve();
|
|
144
|
+
await connection.run('readwrite', (store, tx) => {
|
|
145
|
+
for (const [key, entry] of entries) {
|
|
146
|
+
store.put(entry, key);
|
|
187
147
|
}
|
|
148
|
+
return settled(tx);
|
|
188
149
|
});
|
|
189
150
|
}
|
|
190
151
|
catch (_a) {
|
|
@@ -226,21 +187,11 @@ async function deleteEntries(keys) {
|
|
|
226
187
|
if (!isIndexedDbAvailable() || keys.length === 0)
|
|
227
188
|
return;
|
|
228
189
|
try {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
233
|
-
const store = tx.objectStore(STORE_NAME);
|
|
234
|
-
for (const key of keys) {
|
|
235
|
-
store.delete(key);
|
|
236
|
-
}
|
|
237
|
-
tx.oncomplete = () => resolve();
|
|
238
|
-
tx.onerror = () => resolve();
|
|
239
|
-
tx.onabort = () => resolve();
|
|
240
|
-
}
|
|
241
|
-
catch (_a) {
|
|
242
|
-
resolve();
|
|
190
|
+
await connection.run('readwrite', (store, tx) => {
|
|
191
|
+
for (const key of keys) {
|
|
192
|
+
store.delete(key);
|
|
243
193
|
}
|
|
194
|
+
return settled(tx);
|
|
244
195
|
});
|
|
245
196
|
}
|
|
246
197
|
catch (_a) {
|
|
@@ -256,41 +207,25 @@ export async function clearCachedTranslations(collectionId) {
|
|
|
256
207
|
if (!isIndexedDbAvailable())
|
|
257
208
|
return;
|
|
258
209
|
try {
|
|
259
|
-
const db = await openDb();
|
|
260
210
|
if (!collectionId) {
|
|
261
|
-
await
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
tx.objectStore(STORE_NAME).clear();
|
|
265
|
-
tx.oncomplete = () => resolve();
|
|
266
|
-
tx.onerror = () => resolve();
|
|
267
|
-
tx.onabort = () => resolve();
|
|
268
|
-
}
|
|
269
|
-
catch (_a) {
|
|
270
|
-
resolve();
|
|
271
|
-
}
|
|
211
|
+
await connection.run('readwrite', (store, tx) => {
|
|
212
|
+
store.clear();
|
|
213
|
+
return settled(tx);
|
|
272
214
|
});
|
|
273
215
|
return;
|
|
274
216
|
}
|
|
275
|
-
await new Promise((resolve) => {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
for (const key of request.result) {
|
|
282
|
-
if (key.startsWith(`${collectionId}::`)) {
|
|
283
|
-
store.delete(key);
|
|
284
|
-
}
|
|
217
|
+
await connection.run('readwrite', (store) => new Promise((resolve) => {
|
|
218
|
+
const request = store.getAllKeys();
|
|
219
|
+
request.onsuccess = () => {
|
|
220
|
+
for (const key of request.result) {
|
|
221
|
+
if (key.startsWith(`${collectionId}::`)) {
|
|
222
|
+
store.delete(key);
|
|
285
223
|
}
|
|
286
|
-
|
|
287
|
-
};
|
|
288
|
-
request.onerror = () => resolve();
|
|
289
|
-
}
|
|
290
|
-
catch (_a) {
|
|
224
|
+
}
|
|
291
225
|
resolve();
|
|
292
|
-
}
|
|
293
|
-
|
|
226
|
+
};
|
|
227
|
+
request.onerror = () => resolve();
|
|
228
|
+
}));
|
|
294
229
|
}
|
|
295
230
|
catch (_a) {
|
|
296
231
|
// Ignore deletion failures.
|
package/docs/API_SUMMARY.md
CHANGED
package/docs/theme-tokens.md
CHANGED
|
@@ -99,6 +99,14 @@ renders correctly even against a host that sets none of them.
|
|
|
99
99
|
|
|
100
100
|
Dark mode is a *theme* (a different value-set), not a separate token set — the host swaps the values.
|
|
101
101
|
|
|
102
|
+
- **Host:** in dark mode, send a dark value-set — in the iframe address (`#slt=` with `theme: "dark"`, and the
|
|
103
|
+
legacy `theme=` payload with `m: "d"`) or on the mount root. Sending only the hook (`dark=1`, `.dark`)
|
|
104
|
+
leaves the app to guess.
|
|
105
|
+
- **App:** `theme.css` has dark fallbacks under `.dark` / `[data-theme="dark"]`, so an app built on it stays
|
|
106
|
+
readable even when a host sends the hook and no values. An app with its own CSS variables should do the
|
|
107
|
+
same: a light fallback at the root, a dark one under the hook — never a light colour that also stands
|
|
108
|
+
in for dark.
|
|
109
|
+
|
|
102
110
|
---
|
|
103
111
|
|
|
104
112
|
## 3. Theme value-sets (what the AI emits, what the lookbook stores)
|
package/package.json
CHANGED
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@proveanything/smartlinks",
|
|
3
|
-
"version": "2.1.
|
|
4
|
-
"description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js",
|
|
12
|
-
"default": "./dist/index.js"
|
|
13
|
-
},
|
|
14
|
-
"./testing": {
|
|
15
|
-
"types": "./dist/testing/index.d.ts",
|
|
16
|
-
"import": "./dist/testing/index.js",
|
|
17
|
-
"default": "./dist/testing/index.js"
|
|
18
|
-
},
|
|
19
|
-
"./*": {
|
|
20
|
-
"types": "./dist/*.d.ts",
|
|
21
|
-
"import": "./dist/*.js",
|
|
22
|
-
"default": "./dist/*.js"
|
|
23
|
-
},
|
|
24
|
-
"./baseline.css": "./dist/baseline.css",
|
|
25
|
-
"./baseline.classes.json": "./dist/baseline.classes.json",
|
|
26
|
-
"./theme.css": "./dist/theme.css",
|
|
27
|
-
"./theme-boot.js": "./dist/theme-boot.js",
|
|
28
|
-
"./package.json": "./package.json"
|
|
29
|
-
},
|
|
30
|
-
"bin": {
|
|
31
|
-
"smartlinks-register-release": "scripts/register-release.mjs",
|
|
32
|
-
"smartlinks-publish": "scripts/publish.mjs",
|
|
33
|
-
"smartlinks-doctor": "scripts/doctor.mjs",
|
|
34
|
-
"smartlinks-headless": "scripts/headless-check.mjs"
|
|
35
|
-
},
|
|
36
|
-
"files": [
|
|
37
|
-
"dist/",
|
|
38
|
-
"docs/",
|
|
39
|
-
"scripts/register-release.mjs",
|
|
40
|
-
"scripts/publish.mjs",
|
|
41
|
-
"scripts/doctor.mjs",
|
|
42
|
-
"scripts/headless-check.mjs",
|
|
43
|
-
"scripts/lib/",
|
|
44
|
-
"openapi.yaml",
|
|
45
|
-
"README.md"
|
|
46
|
-
],
|
|
47
|
-
"scripts": {
|
|
48
|
-
"test": "node test/run.cjs",
|
|
49
|
-
"build": "tsc && node scripts/fix-esm-extensions.mjs && node generate-api-summary.cjs && node generate-openapi.cjs && node scripts/copy-docs-to-dist.cjs && node scripts/build-baseline.mjs",
|
|
50
|
-
"docs": "typedoc",
|
|
51
|
-
"docs:summary": "node generate-api-summary.cjs",
|
|
52
|
-
"docs:openapi": "node generate-openapi.cjs",
|
|
53
|
-
"docs:openapi:lint": "npx --yes @redocly/cli@latest lint openapi.yaml",
|
|
54
|
-
"docs:openapi:check": "node generate-openapi.cjs && npx --yes @redocly/cli@latest lint openapi.yaml",
|
|
55
|
-
"build:docs": "tsc build-docs.ts --outDir dist && node dist/build-docs.js",
|
|
56
|
-
"prepublishOnly": "npm run build"
|
|
57
|
-
},
|
|
58
|
-
"keywords": [
|
|
59
|
-
"smartlinks",
|
|
60
|
-
"api",
|
|
61
|
-
"authentication",
|
|
62
|
-
"traceability",
|
|
63
|
-
"blockchain",
|
|
64
|
-
"typescript",
|
|
65
|
-
"sdk"
|
|
66
|
-
],
|
|
67
|
-
"author": "Glenn Shoosmith",
|
|
68
|
-
"license": "MIT",
|
|
69
|
-
"publishConfig": {
|
|
70
|
-
"access": "public"
|
|
71
|
-
},
|
|
72
|
-
"dependencies": {
|
|
73
|
-
"cross-fetch": "^3.1.5",
|
|
74
|
-
"es-module-lexer": "^1.7.0"
|
|
75
|
-
},
|
|
76
|
-
"devDependencies": {
|
|
77
|
-
"ts-node": "^10.9.1",
|
|
78
|
-
"typedoc": "^0.25.13",
|
|
79
|
-
"typedoc-plugin-markdown": "^3.17.1",
|
|
80
|
-
"typescript": "^4.9.0"
|
|
81
|
-
},
|
|
82
|
-
"repository": {
|
|
83
|
-
"type": "git",
|
|
84
|
-
"url": "https://github.com/Prove-Anything/smartlinks.git"
|
|
85
|
-
},
|
|
86
|
-
"homepage": "https://smartlinks.app"
|
|
87
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@proveanything/smartlinks",
|
|
3
|
+
"version": "2.1.10",
|
|
4
|
+
"description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./testing": {
|
|
15
|
+
"types": "./dist/testing/index.d.ts",
|
|
16
|
+
"import": "./dist/testing/index.js",
|
|
17
|
+
"default": "./dist/testing/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./*": {
|
|
20
|
+
"types": "./dist/*.d.ts",
|
|
21
|
+
"import": "./dist/*.js",
|
|
22
|
+
"default": "./dist/*.js"
|
|
23
|
+
},
|
|
24
|
+
"./baseline.css": "./dist/baseline.css",
|
|
25
|
+
"./baseline.classes.json": "./dist/baseline.classes.json",
|
|
26
|
+
"./theme.css": "./dist/theme.css",
|
|
27
|
+
"./theme-boot.js": "./dist/theme-boot.js",
|
|
28
|
+
"./package.json": "./package.json"
|
|
29
|
+
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"smartlinks-register-release": "scripts/register-release.mjs",
|
|
32
|
+
"smartlinks-publish": "scripts/publish.mjs",
|
|
33
|
+
"smartlinks-doctor": "scripts/doctor.mjs",
|
|
34
|
+
"smartlinks-headless": "scripts/headless-check.mjs"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist/",
|
|
38
|
+
"docs/",
|
|
39
|
+
"scripts/register-release.mjs",
|
|
40
|
+
"scripts/publish.mjs",
|
|
41
|
+
"scripts/doctor.mjs",
|
|
42
|
+
"scripts/headless-check.mjs",
|
|
43
|
+
"scripts/lib/",
|
|
44
|
+
"openapi.yaml",
|
|
45
|
+
"README.md"
|
|
46
|
+
],
|
|
47
|
+
"scripts": {
|
|
48
|
+
"test": "node test/run.cjs",
|
|
49
|
+
"build": "tsc && node scripts/fix-esm-extensions.mjs && node generate-api-summary.cjs && node generate-openapi.cjs && node scripts/copy-docs-to-dist.cjs && node scripts/build-baseline.mjs",
|
|
50
|
+
"docs": "typedoc",
|
|
51
|
+
"docs:summary": "node generate-api-summary.cjs",
|
|
52
|
+
"docs:openapi": "node generate-openapi.cjs",
|
|
53
|
+
"docs:openapi:lint": "npx --yes @redocly/cli@latest lint openapi.yaml",
|
|
54
|
+
"docs:openapi:check": "node generate-openapi.cjs && npx --yes @redocly/cli@latest lint openapi.yaml",
|
|
55
|
+
"build:docs": "tsc build-docs.ts --outDir dist && node dist/build-docs.js",
|
|
56
|
+
"prepublishOnly": "npm run build"
|
|
57
|
+
},
|
|
58
|
+
"keywords": [
|
|
59
|
+
"smartlinks",
|
|
60
|
+
"api",
|
|
61
|
+
"authentication",
|
|
62
|
+
"traceability",
|
|
63
|
+
"blockchain",
|
|
64
|
+
"typescript",
|
|
65
|
+
"sdk"
|
|
66
|
+
],
|
|
67
|
+
"author": "Glenn Shoosmith",
|
|
68
|
+
"license": "MIT",
|
|
69
|
+
"publishConfig": {
|
|
70
|
+
"access": "public"
|
|
71
|
+
},
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"cross-fetch": "^3.1.5",
|
|
74
|
+
"es-module-lexer": "^1.7.0"
|
|
75
|
+
},
|
|
76
|
+
"devDependencies": {
|
|
77
|
+
"ts-node": "^10.9.1",
|
|
78
|
+
"typedoc": "^0.25.13",
|
|
79
|
+
"typedoc-plugin-markdown": "^3.17.1",
|
|
80
|
+
"typescript": "^4.9.0"
|
|
81
|
+
},
|
|
82
|
+
"repository": {
|
|
83
|
+
"type": "git",
|
|
84
|
+
"url": "https://github.com/Prove-Anything/smartlinks.git"
|
|
85
|
+
},
|
|
86
|
+
"homepage": "https://smartlinks.app"
|
|
87
|
+
}
|