@rockhall/electron-offline-content 0.4.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/CHANGELOG.md +384 -0
- package/LICENSE +21 -0
- package/README.md +794 -0
- package/dist/internal/asset-file-name.cjs +13 -0
- package/dist/internal/asset-file-name.cjs.map +1 -0
- package/dist/internal/asset-file-name.d.cts +6 -0
- package/dist/internal/asset-file-name.d.cts.map +1 -0
- package/dist/internal/asset-file-name.d.ts +6 -0
- package/dist/internal/asset-file-name.d.ts.map +1 -0
- package/dist/internal/asset-file-name.js +12 -0
- package/dist/internal/asset-file-name.js.map +1 -0
- package/dist/internal/asset-key.cjs +30 -0
- package/dist/internal/asset-key.cjs.map +1 -0
- package/dist/internal/asset-key.d.cts +19 -0
- package/dist/internal/asset-key.d.cts.map +1 -0
- package/dist/internal/asset-key.d.ts +19 -0
- package/dist/internal/asset-key.d.ts.map +1 -0
- package/dist/internal/asset-key.js +27 -0
- package/dist/internal/asset-key.js.map +1 -0
- package/dist/internal/log-format.cjs +98 -0
- package/dist/internal/log-format.cjs.map +1 -0
- package/dist/internal/log-format.d.cts +10 -0
- package/dist/internal/log-format.d.cts.map +1 -0
- package/dist/internal/log-format.d.ts +10 -0
- package/dist/internal/log-format.d.ts.map +1 -0
- package/dist/internal/log-format.js +97 -0
- package/dist/internal/log-format.js.map +1 -0
- package/dist/internal/media-kind.cjs +46 -0
- package/dist/internal/media-kind.cjs.map +1 -0
- package/dist/internal/media-kind.d.cts +20 -0
- package/dist/internal/media-kind.d.cts.map +1 -0
- package/dist/internal/media-kind.d.ts +20 -0
- package/dist/internal/media-kind.d.ts.map +1 -0
- package/dist/internal/media-kind.js +45 -0
- package/dist/internal/media-kind.js.map +1 -0
- package/dist/internal/url-warn.cjs +14 -0
- package/dist/internal/url-warn.cjs.map +1 -0
- package/dist/internal/url-warn.d.cts +10 -0
- package/dist/internal/url-warn.d.cts.map +1 -0
- package/dist/internal/url-warn.d.ts +10 -0
- package/dist/internal/url-warn.d.ts.map +1 -0
- package/dist/internal/url-warn.js +13 -0
- package/dist/internal/url-warn.js.map +1 -0
- package/dist/internal/validation.cjs +222 -0
- package/dist/internal/validation.cjs.map +1 -0
- package/dist/internal/validation.d.cts +78 -0
- package/dist/internal/validation.d.cts.map +1 -0
- package/dist/internal/validation.d.ts +78 -0
- package/dist/internal/validation.d.ts.map +1 -0
- package/dist/internal/validation.js +196 -0
- package/dist/internal/validation.js.map +1 -0
- package/dist/main/asset-download.cjs +265 -0
- package/dist/main/asset-download.cjs.map +1 -0
- package/dist/main/asset-download.d.cts +12 -0
- package/dist/main/asset-download.d.cts.map +1 -0
- package/dist/main/asset-download.d.ts +12 -0
- package/dist/main/asset-download.d.ts.map +1 -0
- package/dist/main/asset-download.js +263 -0
- package/dist/main/asset-download.js.map +1 -0
- package/dist/main/database.cjs +473 -0
- package/dist/main/database.cjs.map +1 -0
- package/dist/main/database.d.cts +81 -0
- package/dist/main/database.d.cts.map +1 -0
- package/dist/main/database.d.ts +81 -0
- package/dist/main/database.d.ts.map +1 -0
- package/dist/main/database.js +472 -0
- package/dist/main/database.js.map +1 -0
- package/dist/main/index.cjs +22 -0
- package/dist/main/index.d.cts +7 -0
- package/dist/main/index.d.ts +7 -0
- package/dist/main/index.js +7 -0
- package/dist/main/media-cache.cjs +862 -0
- package/dist/main/media-cache.cjs.map +1 -0
- package/dist/main/media-cache.d.cts +134 -0
- package/dist/main/media-cache.d.cts.map +1 -0
- package/dist/main/media-cache.d.ts +134 -0
- package/dist/main/media-cache.d.ts.map +1 -0
- package/dist/main/media-cache.js +854 -0
- package/dist/main/media-cache.js.map +1 -0
- package/dist/main/storage-root-lock.cjs +124 -0
- package/dist/main/storage-root-lock.cjs.map +1 -0
- package/dist/main/storage-root-lock.d.cts +11 -0
- package/dist/main/storage-root-lock.d.cts.map +1 -0
- package/dist/main/storage-root-lock.d.ts +11 -0
- package/dist/main/storage-root-lock.d.ts.map +1 -0
- package/dist/main/storage-root-lock.js +120 -0
- package/dist/main/storage-root-lock.js.map +1 -0
- package/dist/main/store.cjs +197 -0
- package/dist/main/store.cjs.map +1 -0
- package/dist/main/store.d.cts +83 -0
- package/dist/main/store.d.cts.map +1 -0
- package/dist/main/store.d.ts +83 -0
- package/dist/main/store.d.ts.map +1 -0
- package/dist/main/store.js +195 -0
- package/dist/main/store.js.map +1 -0
- package/dist/preload/index.cjs +36 -0
- package/dist/preload/index.cjs.map +1 -0
- package/dist/preload/index.d.cts +14 -0
- package/dist/preload/index.d.cts.map +1 -0
- package/dist/preload/index.d.ts +14 -0
- package/dist/preload/index.d.ts.map +1 -0
- package/dist/preload/index.js +34 -0
- package/dist/preload/index.js.map +1 -0
- package/dist/react/index.cjs +199 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +50 -0
- package/dist/react/index.d.cts.map +1 -0
- package/dist/react/index.d.ts +50 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +191 -0
- package/dist/react/index.js.map +1 -0
- package/dist/renderer/helpers.cjs +36 -0
- package/dist/renderer/helpers.cjs.map +1 -0
- package/dist/renderer/helpers.d.cts +11 -0
- package/dist/renderer/helpers.d.cts.map +1 -0
- package/dist/renderer/helpers.d.ts +11 -0
- package/dist/renderer/helpers.d.ts.map +1 -0
- package/dist/renderer/helpers.js +35 -0
- package/dist/renderer/helpers.js.map +1 -0
- package/dist/renderer/index.cjs +20 -0
- package/dist/renderer/index.cjs.map +1 -0
- package/dist/renderer/index.d.cts +14 -0
- package/dist/renderer/index.d.cts.map +1 -0
- package/dist/renderer/index.d.ts +14 -0
- package/dist/renderer/index.d.ts.map +1 -0
- package/dist/renderer/index.js +14 -0
- package/dist/renderer/index.js.map +1 -0
- package/dist/renderer/runtime.cjs +278 -0
- package/dist/renderer/runtime.cjs.map +1 -0
- package/dist/renderer/runtime.d.cts +35 -0
- package/dist/renderer/runtime.d.cts.map +1 -0
- package/dist/renderer/runtime.d.ts +35 -0
- package/dist/renderer/runtime.d.ts.map +1 -0
- package/dist/renderer/runtime.js +273 -0
- package/dist/renderer/runtime.js.map +1 -0
- package/dist/renderer/window-globals.d.cts +9 -0
- package/dist/renderer/window-globals.d.cts.map +1 -0
- package/dist/renderer/window-globals.d.ts +9 -0
- package/dist/renderer/window-globals.d.ts.map +1 -0
- package/dist/shared/errors.cjs +102 -0
- package/dist/shared/errors.cjs.map +1 -0
- package/dist/shared/errors.d.cts +45 -0
- package/dist/shared/errors.d.cts.map +1 -0
- package/dist/shared/errors.d.ts +45 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/errors.js +93 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/ipc.cjs +14 -0
- package/dist/shared/ipc.cjs.map +1 -0
- package/dist/shared/ipc.d.cts +12 -0
- package/dist/shared/ipc.d.cts.map +1 -0
- package/dist/shared/ipc.d.ts +12 -0
- package/dist/shared/ipc.d.ts.map +1 -0
- package/dist/shared/ipc.js +13 -0
- package/dist/shared/ipc.js.map +1 -0
- package/dist/shared/normalize.cjs +19 -0
- package/dist/shared/normalize.cjs.map +1 -0
- package/dist/shared/normalize.d.cts +11 -0
- package/dist/shared/normalize.d.cts.map +1 -0
- package/dist/shared/normalize.d.ts +11 -0
- package/dist/shared/normalize.d.ts.map +1 -0
- package/dist/shared/normalize.js +18 -0
- package/dist/shared/normalize.js.map +1 -0
- package/dist/shared/pagination.cjs +32 -0
- package/dist/shared/pagination.cjs.map +1 -0
- package/dist/shared/pagination.d.cts +14 -0
- package/dist/shared/pagination.d.cts.map +1 -0
- package/dist/shared/pagination.d.ts +14 -0
- package/dist/shared/pagination.d.ts.map +1 -0
- package/dist/shared/pagination.js +28 -0
- package/dist/shared/pagination.js.map +1 -0
- package/dist/shared/stem.cjs +16 -0
- package/dist/shared/stem.cjs.map +1 -0
- package/dist/shared/stem.d.cts +6 -0
- package/dist/shared/stem.d.cts.map +1 -0
- package/dist/shared/stem.d.ts +6 -0
- package/dist/shared/stem.d.ts.map +1 -0
- package/dist/shared/stem.js +14 -0
- package/dist/shared/stem.js.map +1 -0
- package/dist/shared/types.cjs +15 -0
- package/dist/shared/types.cjs.map +1 -0
- package/dist/shared/types.d.cts +234 -0
- package/dist/shared/types.d.cts.map +1 -0
- package/dist/shared/types.d.ts +234 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +14 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +120 -0
- package/skills/authenticated-downloads/SKILL.md +203 -0
- package/skills/cache-configuration/SKILL.md +357 -0
- package/skills/cache-configuration/references/options.md +356 -0
- package/skills/getting-started/SKILL.md +407 -0
- package/skills/production-checklist/SKILL.md +397 -0
- package/skills/react-rendering/SKILL.md +424 -0
- package/skills/react-rendering/references/hooks.md +443 -0
- package/skills/store-authoring/SKILL.md +369 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
# Hooks API Reference
|
|
2
|
+
|
|
3
|
+
Complete reference for the React bindings exported from `@rockhall/electron-offline-content/react`.
|
|
4
|
+
|
|
5
|
+
## Shared Types
|
|
6
|
+
|
|
7
|
+
### AssetKeyInput
|
|
8
|
+
|
|
9
|
+
`string | readonly string[]` — pass the same value to `store.add()` in `resolveStore` and to `useMediaAsset()` / `getAsset()`. Non-empty strings or non-empty arrays of non-empty strings are accepted; there is no further key format validation. Arrays are joined with `/` for `displayKey` on resolved assets.
|
|
10
|
+
|
|
11
|
+
### AsyncState\<T\>
|
|
12
|
+
|
|
13
|
+
Every data-fetching hook returns this shape:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
interface AsyncState<T> {
|
|
17
|
+
data: T | null;
|
|
18
|
+
loading: boolean;
|
|
19
|
+
error: Error | null;
|
|
20
|
+
refresh: () => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- `data` — `null` until the first successful load, then `T`.
|
|
25
|
+
- `loading` — `true` during initial fetch and during `refresh()`.
|
|
26
|
+
- `error` — Set when the underlying IPC call fails; `null` otherwise.
|
|
27
|
+
- `refresh()` — Re-fetches data from the bridge. Returns a promise that resolves when the fetch completes.
|
|
28
|
+
|
|
29
|
+
### MediaCacheProvider
|
|
30
|
+
|
|
31
|
+
Context provider that supplies the `MediaCacheBridge` to all hooks.
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
function MediaCacheProvider({
|
|
35
|
+
bridge,
|
|
36
|
+
children,
|
|
37
|
+
}: PropsWithChildren<{ bridge?: MediaCacheBridge }>): JSX.Element;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| Prop | Type | Description |
|
|
41
|
+
| ---------- | ------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
42
|
+
| `bridge` | `MediaCacheBridge \| undefined` | Optional explicit bridge instance. When omitted, auto-detected from `window.mediaCache`. |
|
|
43
|
+
| `children` | `ReactNode` | Application tree. |
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { MediaCacheProvider } from "@rockhall/electron-offline-content/react";
|
|
47
|
+
|
|
48
|
+
<MediaCacheProvider>
|
|
49
|
+
<App />
|
|
50
|
+
</MediaCacheProvider>;
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Hooks
|
|
56
|
+
|
|
57
|
+
### useMediaBridge
|
|
58
|
+
|
|
59
|
+
Low-level access to the underlying bridge methods with shared status and aggregated errors.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
function useMediaBridge(): UseMediaBridgeResult;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Returns:** `UseMediaBridgeResult`
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
interface UseMediaBridgeResult extends MediaCacheBridge {
|
|
69
|
+
status: AsyncState<MediaCacheStatus>;
|
|
70
|
+
phase: MediaCachePhase;
|
|
71
|
+
errors: MediaCacheErrors;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Throws:** if called outside a `MediaCacheProvider`.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { useMediaBridge } from "@rockhall/electron-offline-content/react";
|
|
79
|
+
|
|
80
|
+
function DebugPanel() {
|
|
81
|
+
const { syncNow, status, errors } = useMediaBridge();
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### useMediaCacheReady
|
|
88
|
+
|
|
89
|
+
Reports whether cached content is available for rendering.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
function useMediaCacheReady(): AsyncState<MediaCacheReadyState>;
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Returns:** `AsyncState<MediaCacheReadyState>`
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
interface MediaCacheReadyState {
|
|
99
|
+
ready: boolean;
|
|
100
|
+
syncing: boolean;
|
|
101
|
+
phase: "idle" | "syncing" | "ready" | "error";
|
|
102
|
+
activeGenerationId: number | null;
|
|
103
|
+
syncError: Error | null;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
| Field | Description |
|
|
108
|
+
| -------------------- | -------------------------------------------------------------------------------- |
|
|
109
|
+
| `ready` | `true` once at least one successful sync has completed and content is available. |
|
|
110
|
+
| `syncing` | `true` while a sync operation is in progress. |
|
|
111
|
+
| `phase` | Current sync lifecycle phase. |
|
|
112
|
+
| `activeGenerationId` | Numeric SQLite generation id, or `null` before the first successful sync. |
|
|
113
|
+
| `syncError` | Error from the most recent sync attempt, or `null`. |
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
import { useMediaCacheReady } from "@rockhall/electron-offline-content/react";
|
|
117
|
+
|
|
118
|
+
function Gate({ children }: { children: React.ReactNode }) {
|
|
119
|
+
const { data, loading } = useMediaCacheReady();
|
|
120
|
+
|
|
121
|
+
if (loading || !data?.ready) {
|
|
122
|
+
return <p>{data?.syncing ? "Downloading…" : "Preparing…"}</p>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return <>{children}</>;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### useMediaCacheStatus
|
|
132
|
+
|
|
133
|
+
Detailed sync status including progress counters.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
function useMediaCacheStatus(): UseMediaCacheStatusResult;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Returns:** `UseMediaCacheStatusResult` — `AsyncState<MediaCacheStatus>` fields plus top-level `phase: MediaCachePhase` (`"loading"` until the first snapshot, then the cache phase).
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
interface MediaCacheStatus {
|
|
143
|
+
phase: "idle" | "syncing" | "ready" | "error";
|
|
144
|
+
storageRoot: string;
|
|
145
|
+
activeGenerationId: number | null;
|
|
146
|
+
progress: SyncProgress | null;
|
|
147
|
+
lastRun: string | null;
|
|
148
|
+
error: string | null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
interface SyncProgress {
|
|
152
|
+
phase:
|
|
153
|
+
| "resolving-store"
|
|
154
|
+
| "staging-generation"
|
|
155
|
+
| "diffing"
|
|
156
|
+
| "downloading"
|
|
157
|
+
| "committing"
|
|
158
|
+
| "pruning";
|
|
159
|
+
totalAssets: number;
|
|
160
|
+
completedAssets: number;
|
|
161
|
+
downloadedAssets: number;
|
|
162
|
+
skippedAssets: number;
|
|
163
|
+
bytesDownloaded: number;
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
| Field | Description |
|
|
168
|
+
| -------------------- | ---------------------------------------------------------------- |
|
|
169
|
+
| `phase` | Current sync lifecycle phase. |
|
|
170
|
+
| `storageRoot` | Absolute path to the local cache directory. |
|
|
171
|
+
| `activeGenerationId` | Numeric id of the content generation being served. |
|
|
172
|
+
| `progress` | Asset-level progress during `"syncing"` phase; `null` otherwise. |
|
|
173
|
+
| `lastRun` | ISO timestamp of the last completed sync, or `null`. |
|
|
174
|
+
| `error` | Error message string from the last sync failure, or `null`. |
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import { useMediaCacheStatus } from "@rockhall/electron-offline-content/react";
|
|
178
|
+
|
|
179
|
+
function SyncProgress() {
|
|
180
|
+
const { data: status, phase } = useMediaCacheStatus();
|
|
181
|
+
|
|
182
|
+
if (phase !== "syncing" || !status?.progress) return null;
|
|
183
|
+
|
|
184
|
+
const pct = Math.round((status.progress.completedAssets / status.progress.totalAssets) * 100);
|
|
185
|
+
|
|
186
|
+
return <p>Syncing: {pct}%</p>;
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
### useMediaAsset
|
|
193
|
+
|
|
194
|
+
Looks up a single asset by `AssetKeyInput` — the same string or segment array passed as the first argument to `store.add()` in `resolveStore`.
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
function useMediaAsset(
|
|
198
|
+
key: AssetKeyInput,
|
|
199
|
+
options?: { refetchOnSyncComplete?: boolean },
|
|
200
|
+
): AsyncState<ResolvedMediaAsset | null>;
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
| Parameter | Type | Description |
|
|
204
|
+
| --------- | --------------- | --------------------------------------------------------------------------- |
|
|
205
|
+
| `key` | `AssetKeyInput` | `string` or `readonly string[]` — must match the key used in `store.add()`. |
|
|
206
|
+
| `options` | `object` | Optional. `refetchOnSyncComplete` re-fetches after sync. |
|
|
207
|
+
|
|
208
|
+
**Returns:** `AsyncState<ResolvedMediaAsset | null>`
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
import { useMediaAsset } from "@rockhall/electron-offline-content/react";
|
|
212
|
+
|
|
213
|
+
function WelcomeVideo() {
|
|
214
|
+
const { data: asset, loading } = useMediaAsset(["video", "welcome"]);
|
|
215
|
+
|
|
216
|
+
if (loading || !asset) return null;
|
|
217
|
+
|
|
218
|
+
return <video src={asset.url} title={asset.displayKey} controls />;
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
### useMediaByIndex
|
|
225
|
+
|
|
226
|
+
Queries assets by secondary index: any name passed to `store.defineIndex()` in `resolveStore`, plus the built-in indexes `mimeType` and `mediaKind` that the store adds for every asset.
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
function useMediaByIndex(
|
|
230
|
+
indexName: string,
|
|
231
|
+
value: string,
|
|
232
|
+
options?: {
|
|
233
|
+
limit?: number;
|
|
234
|
+
cursor?: string;
|
|
235
|
+
refetchOnSyncComplete?: boolean;
|
|
236
|
+
},
|
|
237
|
+
): AsyncState<PaginationResult<ResolvedMediaAsset>>;
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
| Parameter | Type | Description |
|
|
241
|
+
| ----------- | -------- | -------------------------------------------------- |
|
|
242
|
+
| `indexName` | `string` | Name of the index (as defined by `defineIndex()`). |
|
|
243
|
+
| `value` | `string` | The index value to match. |
|
|
244
|
+
| `options` | `object` | Optional pagination and refetch options. |
|
|
245
|
+
|
|
246
|
+
**Options:**
|
|
247
|
+
|
|
248
|
+
| Option | Type | Default | Description |
|
|
249
|
+
| ----------------------- | --------- | ------- | ------------------------------ |
|
|
250
|
+
| `limit` | `number` | — | Maximum results per page. |
|
|
251
|
+
| `cursor` | `string` | — | Opaque cursor for next page. |
|
|
252
|
+
| `refetchOnSyncComplete` | `boolean` | `true` | Re-fetch after sync completes. |
|
|
253
|
+
|
|
254
|
+
**Returns:** `AsyncState<PaginationResult<ResolvedMediaAsset>>`
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
import { useMediaByIndex } from "@rockhall/electron-offline-content/react";
|
|
258
|
+
|
|
259
|
+
function ExhibitList() {
|
|
260
|
+
const { data, loading } = useMediaByIndex("category", "exhibits", {
|
|
261
|
+
limit: 30,
|
|
262
|
+
refetchOnSyncComplete: true,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
if (loading || !data) return <p>Loading…</p>;
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<>
|
|
269
|
+
{data.items.map((asset) => (
|
|
270
|
+
<div key={asset.key}>
|
|
271
|
+
<img src={asset.url} alt={(asset.metadata.title as string) ?? asset.displayKey} />
|
|
272
|
+
</div>
|
|
273
|
+
))}
|
|
274
|
+
</>
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
### useFileStemMatch
|
|
282
|
+
|
|
283
|
+
Searches cached content by filename stem across all assets.
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
function useFileStemMatch(
|
|
287
|
+
stem: string,
|
|
288
|
+
options?: FileStemMatchQueryOptions,
|
|
289
|
+
): AsyncState<PaginationResult<FileStemMatch>>;
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
| Parameter | Type | Description |
|
|
293
|
+
| --------- | ---------------------------------------- | ------------------------------------------------ |
|
|
294
|
+
| `stem` | `string` | Filename stem to search for (without extension). |
|
|
295
|
+
| `options` | `FileStemMatchQueryOptions \| undefined` | Filtering and pagination options. |
|
|
296
|
+
|
|
297
|
+
**FileStemMatchQueryOptions:**
|
|
298
|
+
|
|
299
|
+
| Option | Type | Default | Description |
|
|
300
|
+
| ----------------------- | --------- | ------- | ------------------------------ |
|
|
301
|
+
| `limit` | `number` | — | Maximum matches per page. |
|
|
302
|
+
| `cursor` | `string` | — | Opaque cursor for next page. |
|
|
303
|
+
| `refetchOnSyncComplete` | `boolean` | `true` | Re-fetch after sync completes. |
|
|
304
|
+
|
|
305
|
+
**Returns:** `AsyncState<PaginationResult<FileStemMatch>>`
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
import { useFileStemMatch } from "@rockhall/electron-offline-content/react";
|
|
309
|
+
|
|
310
|
+
function Search({ query }: { query: string }) {
|
|
311
|
+
const { data, loading } = useFileStemMatch(query, { limit: 20 });
|
|
312
|
+
|
|
313
|
+
if (loading || !data) return <p>Searching…</p>;
|
|
314
|
+
|
|
315
|
+
return (
|
|
316
|
+
<ul>
|
|
317
|
+
{data.items.map((match) => (
|
|
318
|
+
<li key={match.asset.key}>{match.asset.displayKey}</li>
|
|
319
|
+
))}
|
|
320
|
+
</ul>
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
### useMediaCacheErrors
|
|
328
|
+
|
|
329
|
+
Aggregates sync errors and provider-wide query errors without requiring caller arguments.
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
function useMediaCacheErrors(): MediaCacheErrors;
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Returns:** `MediaCacheErrors`
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
interface MediaCacheErrors {
|
|
339
|
+
syncError: Error | null;
|
|
340
|
+
statusError: Error | null;
|
|
341
|
+
queryErrors: Error[];
|
|
342
|
+
hasError: boolean;
|
|
343
|
+
primaryError: Error | null;
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
| Field | Description |
|
|
348
|
+
| -------------- | ------------------------------------------------------------------------------------------ |
|
|
349
|
+
| `syncError` | Error from the sync process itself (from the shared provider status). |
|
|
350
|
+
| `statusError` | Error fetching status. |
|
|
351
|
+
| `queryErrors` | Array of non-null errors from mounted query hooks under the same `MediaCacheProvider`. |
|
|
352
|
+
| `hasError` | `true` if any of the above are set. |
|
|
353
|
+
| `primaryError` | First available error in priority order: `statusError` → first `queryError` → `syncError`. |
|
|
354
|
+
|
|
355
|
+
```tsx
|
|
356
|
+
import { useMediaByIndex, useMediaCacheErrors } from "@rockhall/electron-offline-content/react";
|
|
357
|
+
|
|
358
|
+
function Page() {
|
|
359
|
+
const videos = useMediaByIndex("category", "videos");
|
|
360
|
+
const images = useMediaByIndex("category", "images");
|
|
361
|
+
const errors = useMediaCacheErrors();
|
|
362
|
+
|
|
363
|
+
if (errors.hasError) {
|
|
364
|
+
return <p>Error: {errors.primaryError?.message}</p>;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// render content
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Return Types
|
|
374
|
+
|
|
375
|
+
### ResolvedMediaAsset
|
|
376
|
+
|
|
377
|
+
A single resolved asset with a ready-to-render URL.
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
interface ResolvedMediaAsset {
|
|
381
|
+
key: string;
|
|
382
|
+
displayKey: string;
|
|
383
|
+
version: string;
|
|
384
|
+
kind: MediaKind;
|
|
385
|
+
mimeType: string;
|
|
386
|
+
byteLength?: number;
|
|
387
|
+
url: string;
|
|
388
|
+
indexes: Record<string, string | string[]>;
|
|
389
|
+
metadata: Record<string, JsonValue>;
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
| Field | Description |
|
|
394
|
+
| ------------ | -------------------------------------------------------------------------------- |
|
|
395
|
+
| `key` | Stable storage identity (SHA-256–derived hash, 16 hex chars). |
|
|
396
|
+
| `displayKey` | Original human-readable key (`string` input, or array segments joined with `/`). |
|
|
397
|
+
| `version` | Content version string from the store. |
|
|
398
|
+
| `kind` | Media kind enum value (e.g. `"video"`, `"image"`, `"audio"`, `"document"`). |
|
|
399
|
+
| `mimeType` | MIME type (e.g. `"video/mp4"`). |
|
|
400
|
+
| `byteLength` | Size in bytes when known from the store. |
|
|
401
|
+
| `url` | Ready-to-render URL. `media://` in offline mode, HTTPS in devPassthrough mode. |
|
|
402
|
+
| `indexes` | Index names to their values; arrays for multi-cardinality indexes. |
|
|
403
|
+
| `metadata` | Arbitrary JSON metadata from `store.add()`. |
|
|
404
|
+
|
|
405
|
+
### FileStemMatch
|
|
406
|
+
|
|
407
|
+
A filename stem search result.
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
interface FileStemMatch {
|
|
411
|
+
asset: ResolvedMediaAsset;
|
|
412
|
+
score: number;
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
| Field | Description |
|
|
417
|
+
| ------- | -------------------------------------------------------- |
|
|
418
|
+
| `asset` | The matched `ResolvedMediaAsset`. |
|
|
419
|
+
| `score` | Relevance score for the match (higher is more relevant). |
|
|
420
|
+
|
|
421
|
+
### MediaCacheReadyState
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
interface MediaCacheReadyState {
|
|
425
|
+
ready: boolean;
|
|
426
|
+
syncing: boolean;
|
|
427
|
+
phase: "idle" | "syncing" | "ready" | "error";
|
|
428
|
+
activeGenerationId: number | null;
|
|
429
|
+
syncError: Error | null;
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### MediaCacheErrors
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
interface MediaCacheErrors {
|
|
437
|
+
syncError: Error | null;
|
|
438
|
+
statusError: Error | null;
|
|
439
|
+
queryErrors: Error[];
|
|
440
|
+
hasError: boolean;
|
|
441
|
+
primaryError: Error | null;
|
|
442
|
+
}
|
|
443
|
+
```
|