@pancake-apps/web 0.0.0-snapshot-20260125200133
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 +333 -0
- package/dist/adaptor-interface-BYbH9PpT.d.ts +370 -0
- package/dist/apps-sdk/index.d.ts +1 -0
- package/dist/apps-sdk/index.js +4 -0
- package/dist/apps-sdk/index.js.map +1 -0
- package/dist/chunk-5NYJ2IVD.js +406 -0
- package/dist/chunk-5NYJ2IVD.js.map +1 -0
- package/dist/chunk-7HJ5PKKT.js +146 -0
- package/dist/chunk-7HJ5PKKT.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +9 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-TLBYYZHP.js +715 -0
- package/dist/chunk-TLBYYZHP.js.map +1 -0
- package/dist/chunk-YGGRUIUG.js +977 -0
- package/dist/chunk-YGGRUIUG.js.map +1 -0
- package/dist/chunk-ZYBPDIEG.js +674 -0
- package/dist/chunk-ZYBPDIEG.js.map +1 -0
- package/dist/core/index.d.ts +169 -0
- package/dist/core/index.js +195 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index-BBfZZJWn.d.ts +671 -0
- package/dist/index-CpXDfXKD.d.ts +395 -0
- package/dist/index-DtukOUjY.d.ts +738 -0
- package/dist/index.d.ts +741 -0
- package/dist/index.js +685 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-apps/index.d.ts +2 -0
- package/dist/mcp-apps/index.js +4 -0
- package/dist/mcp-apps/index.js.map +1 -0
- package/dist/notify-size-changed-Ck2BGfUk.d.ts +270 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.js +7 -0
- package/dist/react/index.js.map +1 -0
- package/dist/types-B_O3kZYh.d.ts +253 -0
- package/package.json +90 -0
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
import { resetAdaptor, getAdaptor, getPlatform } from './chunk-ZYBPDIEG.js';
|
|
2
|
+
import { getBridge, getStore, MCP_METHODS } from './chunk-YGGRUIUG.js';
|
|
3
|
+
import { useState, useRef, useEffect, useCallback, useSyncExternalStore } from 'react';
|
|
4
|
+
|
|
5
|
+
// src/unified/view.ts
|
|
6
|
+
var View = class {
|
|
7
|
+
adaptor;
|
|
8
|
+
options;
|
|
9
|
+
_isConnected = false;
|
|
10
|
+
cleanupFns = [];
|
|
11
|
+
_hostContext;
|
|
12
|
+
_hostCapabilities;
|
|
13
|
+
_hostInfo;
|
|
14
|
+
// ============================================
|
|
15
|
+
// Event Handlers
|
|
16
|
+
// ============================================
|
|
17
|
+
/**
|
|
18
|
+
* Called when tool input is received (complete input)
|
|
19
|
+
*/
|
|
20
|
+
ontoolinput;
|
|
21
|
+
/**
|
|
22
|
+
* Called during streaming when partial tool input is received (MCP only)
|
|
23
|
+
*/
|
|
24
|
+
ontoolinputpartial;
|
|
25
|
+
/**
|
|
26
|
+
* Called when tool result is received
|
|
27
|
+
*/
|
|
28
|
+
ontoolresult;
|
|
29
|
+
/**
|
|
30
|
+
* Called when tool execution is cancelled
|
|
31
|
+
*/
|
|
32
|
+
ontoolcancelled;
|
|
33
|
+
/**
|
|
34
|
+
* Called when host context changes (theme, display mode, etc.)
|
|
35
|
+
*/
|
|
36
|
+
onhostcontextchanged;
|
|
37
|
+
/**
|
|
38
|
+
* Called when the host requests teardown
|
|
39
|
+
*/
|
|
40
|
+
onteardown;
|
|
41
|
+
/**
|
|
42
|
+
* Called when an error occurs
|
|
43
|
+
*/
|
|
44
|
+
onerror;
|
|
45
|
+
/**
|
|
46
|
+
* Called when the connection is closed
|
|
47
|
+
*/
|
|
48
|
+
onclose;
|
|
49
|
+
// ============================================
|
|
50
|
+
// Constructor
|
|
51
|
+
// ============================================
|
|
52
|
+
constructor(options = {}) {
|
|
53
|
+
this.options = options;
|
|
54
|
+
if (options.platform) {
|
|
55
|
+
resetAdaptor();
|
|
56
|
+
}
|
|
57
|
+
this.adaptor = getAdaptor();
|
|
58
|
+
}
|
|
59
|
+
// ============================================
|
|
60
|
+
// Getters
|
|
61
|
+
// ============================================
|
|
62
|
+
/**
|
|
63
|
+
* Get the current host context (MCP-specific)
|
|
64
|
+
*/
|
|
65
|
+
getHostContext() {
|
|
66
|
+
return this._hostContext;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get host capabilities (MCP-specific)
|
|
70
|
+
*/
|
|
71
|
+
getHostCapabilities() {
|
|
72
|
+
return this._hostCapabilities;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get host info (MCP-specific)
|
|
76
|
+
*/
|
|
77
|
+
getHostInfo() {
|
|
78
|
+
return this._hostInfo;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Check if the view is connected
|
|
82
|
+
*/
|
|
83
|
+
isConnected() {
|
|
84
|
+
return this._isConnected;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get the current platform
|
|
88
|
+
*/
|
|
89
|
+
getPlatform() {
|
|
90
|
+
return getPlatform();
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get the underlying adaptor for advanced use cases
|
|
94
|
+
*/
|
|
95
|
+
getAdaptor() {
|
|
96
|
+
return this.adaptor;
|
|
97
|
+
}
|
|
98
|
+
// ============================================
|
|
99
|
+
// Context Getters
|
|
100
|
+
// ============================================
|
|
101
|
+
/**
|
|
102
|
+
* Get the current theme
|
|
103
|
+
*/
|
|
104
|
+
getTheme() {
|
|
105
|
+
return this.adaptor.getTheme();
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get the current locale
|
|
109
|
+
*/
|
|
110
|
+
getLocale() {
|
|
111
|
+
return this.adaptor.getLocale();
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get the current display mode
|
|
115
|
+
*/
|
|
116
|
+
getDisplayMode() {
|
|
117
|
+
return this.adaptor.getDisplayMode();
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get the max height constraint
|
|
121
|
+
*/
|
|
122
|
+
getMaxHeight() {
|
|
123
|
+
return this.adaptor.getMaxHeight();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get safe area insets
|
|
127
|
+
*/
|
|
128
|
+
getSafeArea() {
|
|
129
|
+
return this.adaptor.getSafeArea();
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get the normalized platform
|
|
133
|
+
*/
|
|
134
|
+
getNormalizedPlatform() {
|
|
135
|
+
return this.adaptor.getPlatform();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get the device type
|
|
139
|
+
*/
|
|
140
|
+
getDeviceType() {
|
|
141
|
+
return this.adaptor.getDeviceType();
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get container size
|
|
145
|
+
*/
|
|
146
|
+
getContainerSize() {
|
|
147
|
+
return this.adaptor.getContainerSize();
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get input capabilities
|
|
151
|
+
*/
|
|
152
|
+
getInputCapabilities() {
|
|
153
|
+
return this.adaptor.getInputCapabilities();
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get the current tool input
|
|
157
|
+
*/
|
|
158
|
+
getToolInput() {
|
|
159
|
+
return this.adaptor.getToolInput();
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get partial tool input during streaming (MCP only)
|
|
163
|
+
*/
|
|
164
|
+
getToolInputPartial() {
|
|
165
|
+
return this.adaptor.getToolInputPartial();
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get the current tool output
|
|
169
|
+
*/
|
|
170
|
+
getToolOutput() {
|
|
171
|
+
return this.adaptor.getToolOutput();
|
|
172
|
+
}
|
|
173
|
+
// ============================================
|
|
174
|
+
// Connection
|
|
175
|
+
// ============================================
|
|
176
|
+
/**
|
|
177
|
+
* Connect to the host platform.
|
|
178
|
+
* For MCP, this performs the initialization handshake.
|
|
179
|
+
* For Apps SDK, this is a no-op (already connected).
|
|
180
|
+
*/
|
|
181
|
+
async connect() {
|
|
182
|
+
if (this._isConnected) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
if (this.adaptor.platform === "mcp-apps") {
|
|
187
|
+
await this.connectMcp();
|
|
188
|
+
} else {
|
|
189
|
+
await this.connectAppsSdk();
|
|
190
|
+
}
|
|
191
|
+
this._isConnected = true;
|
|
192
|
+
} catch (error) {
|
|
193
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
194
|
+
this.onerror?.(err);
|
|
195
|
+
throw err;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async connectMcp() {
|
|
199
|
+
const bridge = getBridge();
|
|
200
|
+
const store = getStore();
|
|
201
|
+
const initResult = await bridge.initialize({
|
|
202
|
+
clientInfo: this.options.appInfo ?? { name: "pancake-app", version: "1.0.0" },
|
|
203
|
+
capabilities: this.options.capabilities ?? {}
|
|
204
|
+
});
|
|
205
|
+
this._hostContext = initResult.hostContext;
|
|
206
|
+
this._hostCapabilities = initResult.hostCapabilities;
|
|
207
|
+
this._hostInfo = initResult.hostInfo;
|
|
208
|
+
store.setInitialized(initResult.hostContext, initResult.hostCapabilities, initResult.hostInfo);
|
|
209
|
+
this.cleanupFns.push(
|
|
210
|
+
bridge.onNotification(MCP_METHODS.TOOL_INPUT, (params) => {
|
|
211
|
+
this.ontoolinput?.(params);
|
|
212
|
+
})
|
|
213
|
+
);
|
|
214
|
+
this.cleanupFns.push(
|
|
215
|
+
bridge.onNotification(MCP_METHODS.TOOL_INPUT_PARTIAL, (params) => {
|
|
216
|
+
this.ontoolinputpartial?.(params);
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
this.cleanupFns.push(
|
|
220
|
+
bridge.onNotification(MCP_METHODS.TOOL_RESULT, (params) => {
|
|
221
|
+
this.ontoolresult?.(params);
|
|
222
|
+
})
|
|
223
|
+
);
|
|
224
|
+
this.cleanupFns.push(
|
|
225
|
+
bridge.onNotification(MCP_METHODS.TOOL_CANCELLED, (params) => {
|
|
226
|
+
this.ontoolcancelled?.(params);
|
|
227
|
+
})
|
|
228
|
+
);
|
|
229
|
+
this.cleanupFns.push(
|
|
230
|
+
bridge.onNotification(MCP_METHODS.HOST_CONTEXT_CHANGED, (params) => {
|
|
231
|
+
const changes = params;
|
|
232
|
+
this._hostContext = { ...this._hostContext, ...changes };
|
|
233
|
+
this.onhostcontextchanged?.(this._hostContext);
|
|
234
|
+
})
|
|
235
|
+
);
|
|
236
|
+
if (this.onteardown) {
|
|
237
|
+
this.cleanupFns.push(bridge.onTeardown(this.onteardown));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async connectAppsSdk() {
|
|
241
|
+
await this.adaptor.ensureReady();
|
|
242
|
+
this._hostContext = {
|
|
243
|
+
theme: this.adaptor.getTheme(),
|
|
244
|
+
displayMode: this.adaptor.getDisplayMode(),
|
|
245
|
+
locale: this.adaptor.getLocale()
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Close the view and cleanup resources
|
|
250
|
+
*/
|
|
251
|
+
close() {
|
|
252
|
+
this.cleanupFns.forEach((fn) => fn());
|
|
253
|
+
this.cleanupFns = [];
|
|
254
|
+
this._isConnected = false;
|
|
255
|
+
this.onclose?.();
|
|
256
|
+
}
|
|
257
|
+
// ============================================
|
|
258
|
+
// Actions
|
|
259
|
+
// ============================================
|
|
260
|
+
/**
|
|
261
|
+
* Call a server tool
|
|
262
|
+
*/
|
|
263
|
+
async callTool(name, args) {
|
|
264
|
+
this.ensureConnected();
|
|
265
|
+
return this.adaptor.callTool(name, args);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Send a message to the chat
|
|
269
|
+
*/
|
|
270
|
+
async sendMessage(content) {
|
|
271
|
+
this.ensureConnected();
|
|
272
|
+
if (typeof content === "string") {
|
|
273
|
+
this.adaptor.sendFollowUpMessage(content);
|
|
274
|
+
} else {
|
|
275
|
+
const text = content.filter((block) => block.type === "text" && !!block.text).map((block) => block.text).join("\n");
|
|
276
|
+
this.adaptor.sendFollowUpMessage(text);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Open a link in the browser
|
|
281
|
+
*/
|
|
282
|
+
async openLink(url) {
|
|
283
|
+
this.ensureConnected();
|
|
284
|
+
this.adaptor.openExternal(url);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Request a display mode change
|
|
288
|
+
*/
|
|
289
|
+
async requestDisplayMode(mode) {
|
|
290
|
+
this.ensureConnected();
|
|
291
|
+
if (this.adaptor.platform === "mcp-apps") {
|
|
292
|
+
const bridge = getBridge();
|
|
293
|
+
return bridge.requestDisplayMode(mode);
|
|
294
|
+
} else {
|
|
295
|
+
this.adaptor.requestDisplayMode(mode);
|
|
296
|
+
return mode;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Update the model context (MCP only)
|
|
301
|
+
*/
|
|
302
|
+
async updateModelContext(content, structured) {
|
|
303
|
+
this.ensureConnected();
|
|
304
|
+
const params = {};
|
|
305
|
+
if (content) params.content = content;
|
|
306
|
+
if (structured) params.structuredContent = structured;
|
|
307
|
+
await this.adaptor.updateModelContext(params);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Read a resource (MCP only)
|
|
311
|
+
*/
|
|
312
|
+
async readResource(uri) {
|
|
313
|
+
this.ensureConnected();
|
|
314
|
+
return this.adaptor.readResource(uri);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Notify the host of size changes
|
|
318
|
+
*/
|
|
319
|
+
notifySizeChanged(width, height) {
|
|
320
|
+
this.adaptor.notifySizeChanged(width, height);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Setup automatic size reporting
|
|
324
|
+
*/
|
|
325
|
+
setupAutoSizeReporting(element) {
|
|
326
|
+
const el = element ?? (typeof document !== "undefined" ? document.documentElement : void 0);
|
|
327
|
+
if (!el) return () => {
|
|
328
|
+
};
|
|
329
|
+
return this.adaptor.setupAutoSizeReporting(el);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Log a message to the host
|
|
333
|
+
*/
|
|
334
|
+
log(level, data, logger) {
|
|
335
|
+
this.adaptor.log(level, data, logger);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Ping the host (MCP only)
|
|
339
|
+
*/
|
|
340
|
+
async ping() {
|
|
341
|
+
await this.adaptor.ping();
|
|
342
|
+
}
|
|
343
|
+
// ============================================
|
|
344
|
+
// Private Helpers
|
|
345
|
+
// ============================================
|
|
346
|
+
ensureConnected() {
|
|
347
|
+
if (!this._isConnected) {
|
|
348
|
+
throw new Error("View is not connected. Call connect() first.");
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
async function createView(options = {}) {
|
|
353
|
+
const { autoConnect = false, ...viewOptions } = options;
|
|
354
|
+
const view = new View(viewOptions);
|
|
355
|
+
if (autoConnect) {
|
|
356
|
+
await view.connect();
|
|
357
|
+
}
|
|
358
|
+
return view;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// src/unified/react/use-view.ts
|
|
362
|
+
function useView(options = {}) {
|
|
363
|
+
const {
|
|
364
|
+
autoConnect = true,
|
|
365
|
+
onViewCreated,
|
|
366
|
+
onConnected,
|
|
367
|
+
onError,
|
|
368
|
+
onClose,
|
|
369
|
+
...viewOptions
|
|
370
|
+
} = options;
|
|
371
|
+
const [view, setView] = useState(null);
|
|
372
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
373
|
+
const [isConnecting, setIsConnecting] = useState(false);
|
|
374
|
+
const [error, setError] = useState(null);
|
|
375
|
+
const onViewCreatedRef = useRef(onViewCreated);
|
|
376
|
+
const onConnectedRef = useRef(onConnected);
|
|
377
|
+
const onErrorRef = useRef(onError);
|
|
378
|
+
const onCloseRef = useRef(onClose);
|
|
379
|
+
onViewCreatedRef.current = onViewCreated;
|
|
380
|
+
onConnectedRef.current = onConnected;
|
|
381
|
+
onErrorRef.current = onError;
|
|
382
|
+
onCloseRef.current = onClose;
|
|
383
|
+
useEffect(() => {
|
|
384
|
+
const newView = new View(viewOptions);
|
|
385
|
+
newView.onclose = () => {
|
|
386
|
+
setIsConnected(false);
|
|
387
|
+
onCloseRef.current?.();
|
|
388
|
+
};
|
|
389
|
+
newView.onerror = (err) => {
|
|
390
|
+
setError(err);
|
|
391
|
+
onErrorRef.current?.(err);
|
|
392
|
+
};
|
|
393
|
+
onViewCreatedRef.current?.(newView);
|
|
394
|
+
setView(newView);
|
|
395
|
+
return () => {
|
|
396
|
+
newView.close();
|
|
397
|
+
};
|
|
398
|
+
}, []);
|
|
399
|
+
const connect = useCallback(async () => {
|
|
400
|
+
if (!view || isConnecting || isConnected) return;
|
|
401
|
+
setIsConnecting(true);
|
|
402
|
+
setError(null);
|
|
403
|
+
try {
|
|
404
|
+
await view.connect();
|
|
405
|
+
setIsConnected(true);
|
|
406
|
+
onConnectedRef.current?.(view);
|
|
407
|
+
} catch (err) {
|
|
408
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
409
|
+
setError(error2);
|
|
410
|
+
onErrorRef.current?.(error2);
|
|
411
|
+
} finally {
|
|
412
|
+
setIsConnecting(false);
|
|
413
|
+
}
|
|
414
|
+
}, [view, isConnecting, isConnected]);
|
|
415
|
+
const disconnect = useCallback(() => {
|
|
416
|
+
if (view && isConnected) {
|
|
417
|
+
view.close();
|
|
418
|
+
setIsConnected(false);
|
|
419
|
+
}
|
|
420
|
+
}, [view, isConnected]);
|
|
421
|
+
useEffect(() => {
|
|
422
|
+
if (view && autoConnect && !isConnected && !isConnecting) {
|
|
423
|
+
connect();
|
|
424
|
+
}
|
|
425
|
+
}, [view, autoConnect, isConnected, isConnecting, connect]);
|
|
426
|
+
return {
|
|
427
|
+
view,
|
|
428
|
+
isConnected,
|
|
429
|
+
isConnecting,
|
|
430
|
+
error,
|
|
431
|
+
connect,
|
|
432
|
+
disconnect
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// src/unified/style-utils.ts
|
|
437
|
+
function getDocumentTheme() {
|
|
438
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
439
|
+
return "light";
|
|
440
|
+
}
|
|
441
|
+
const colorSchemeMeta = document.querySelector('meta[name="color-scheme"]');
|
|
442
|
+
if (colorSchemeMeta) {
|
|
443
|
+
const content = colorSchemeMeta.getAttribute("content");
|
|
444
|
+
if (content?.includes("dark")) return "dark";
|
|
445
|
+
if (content?.includes("light")) return "light";
|
|
446
|
+
}
|
|
447
|
+
const docElement = document.documentElement;
|
|
448
|
+
if (docElement.classList.contains("dark")) return "dark";
|
|
449
|
+
if (docElement.classList.contains("light")) return "light";
|
|
450
|
+
const dataTheme = docElement.getAttribute("data-theme");
|
|
451
|
+
if (dataTheme === "dark") return "dark";
|
|
452
|
+
if (dataTheme === "light") return "light";
|
|
453
|
+
if (window.matchMedia?.("(prefers-color-scheme: dark)").matches) {
|
|
454
|
+
return "dark";
|
|
455
|
+
}
|
|
456
|
+
return "light";
|
|
457
|
+
}
|
|
458
|
+
function observeDocumentTheme(callback) {
|
|
459
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
460
|
+
return {
|
|
461
|
+
getTheme: () => "light",
|
|
462
|
+
disconnect: () => {
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
let currentTheme = getDocumentTheme();
|
|
467
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
468
|
+
const handleMediaChange = () => {
|
|
469
|
+
const newTheme = getDocumentTheme();
|
|
470
|
+
if (newTheme !== currentTheme) {
|
|
471
|
+
currentTheme = newTheme;
|
|
472
|
+
callback(newTheme);
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
const observer = new MutationObserver(() => {
|
|
476
|
+
const newTheme = getDocumentTheme();
|
|
477
|
+
if (newTheme !== currentTheme) {
|
|
478
|
+
currentTheme = newTheme;
|
|
479
|
+
callback(newTheme);
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
mediaQuery.addEventListener("change", handleMediaChange);
|
|
483
|
+
observer.observe(document.documentElement, {
|
|
484
|
+
attributes: true,
|
|
485
|
+
attributeFilter: ["class", "data-theme"]
|
|
486
|
+
});
|
|
487
|
+
return {
|
|
488
|
+
getTheme: () => currentTheme,
|
|
489
|
+
disconnect: () => {
|
|
490
|
+
mediaQuery.removeEventListener("change", handleMediaChange);
|
|
491
|
+
observer.disconnect();
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
function applyDocumentTheme(theme) {
|
|
496
|
+
if (typeof document === "undefined") return;
|
|
497
|
+
const docElement = document.documentElement;
|
|
498
|
+
docElement.classList.remove("light", "dark");
|
|
499
|
+
docElement.classList.add(theme);
|
|
500
|
+
docElement.setAttribute("data-theme", theme);
|
|
501
|
+
docElement.style.colorScheme = theme;
|
|
502
|
+
}
|
|
503
|
+
var STYLE_ELEMENT_ID = "pancake-host-styles";
|
|
504
|
+
var FONTS_STYLE_ELEMENT_ID = "pancake-host-fonts";
|
|
505
|
+
function applyHostStyleVariables(variables) {
|
|
506
|
+
if (typeof document === "undefined") return;
|
|
507
|
+
let styleElement = document.getElementById(STYLE_ELEMENT_ID);
|
|
508
|
+
if (!styleElement) {
|
|
509
|
+
styleElement = document.createElement("style");
|
|
510
|
+
styleElement.id = STYLE_ELEMENT_ID;
|
|
511
|
+
document.head.appendChild(styleElement);
|
|
512
|
+
}
|
|
513
|
+
const cssVars = Object.entries(variables).map(([key, value]) => ` ${key}: ${value};`).join("\n");
|
|
514
|
+
styleElement.textContent = `:root {
|
|
515
|
+
${cssVars}
|
|
516
|
+
}`;
|
|
517
|
+
}
|
|
518
|
+
function applyHostFonts(css) {
|
|
519
|
+
if (typeof document === "undefined") return;
|
|
520
|
+
let styleElement = document.getElementById(FONTS_STYLE_ELEMENT_ID);
|
|
521
|
+
if (!styleElement) {
|
|
522
|
+
styleElement = document.createElement("style");
|
|
523
|
+
styleElement.id = FONTS_STYLE_ELEMENT_ID;
|
|
524
|
+
document.head.appendChild(styleElement);
|
|
525
|
+
}
|
|
526
|
+
styleElement.textContent = css;
|
|
527
|
+
}
|
|
528
|
+
function clearHostStyles() {
|
|
529
|
+
if (typeof document === "undefined") return;
|
|
530
|
+
document.getElementById(STYLE_ELEMENT_ID)?.remove();
|
|
531
|
+
document.getElementById(FONTS_STYLE_ELEMENT_ID)?.remove();
|
|
532
|
+
}
|
|
533
|
+
function applyHostContext(context) {
|
|
534
|
+
if (!context) return;
|
|
535
|
+
if (context.theme) {
|
|
536
|
+
applyDocumentTheme(context.theme);
|
|
537
|
+
}
|
|
538
|
+
if (context.styles?.variables) {
|
|
539
|
+
applyHostStyleVariables(context.styles.variables);
|
|
540
|
+
}
|
|
541
|
+
if (context.styles?.css?.fonts) {
|
|
542
|
+
applyHostFonts(context.styles.css.fonts);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function getComputedStyleVariables(element, variableNames) {
|
|
546
|
+
if (typeof window === "undefined") return {};
|
|
547
|
+
const el = element ?? document.documentElement;
|
|
548
|
+
const computedStyle = window.getComputedStyle(el);
|
|
549
|
+
const result = {};
|
|
550
|
+
if (variableNames) {
|
|
551
|
+
for (const name of variableNames) {
|
|
552
|
+
const value = computedStyle.getPropertyValue(name).trim();
|
|
553
|
+
if (value) {
|
|
554
|
+
result[name] = value;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
} else {
|
|
558
|
+
const cssText = el.style.cssText;
|
|
559
|
+
const matches = cssText.matchAll(/(--[\w-]+):\s*([^;]+)/g);
|
|
560
|
+
for (const match of matches) {
|
|
561
|
+
const key = match[1];
|
|
562
|
+
const value = match[2];
|
|
563
|
+
if (key && value) {
|
|
564
|
+
result[key] = value.trim();
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return result;
|
|
569
|
+
}
|
|
570
|
+
function applySafeAreaInsets(insets) {
|
|
571
|
+
if (typeof document === "undefined") return;
|
|
572
|
+
const docElement = document.documentElement;
|
|
573
|
+
docElement.style.setProperty("--safe-area-inset-top", `${insets.top}px`);
|
|
574
|
+
docElement.style.setProperty("--safe-area-inset-right", `${insets.right}px`);
|
|
575
|
+
docElement.style.setProperty("--safe-area-inset-bottom", `${insets.bottom}px`);
|
|
576
|
+
docElement.style.setProperty("--safe-area-inset-left", `${insets.left}px`);
|
|
577
|
+
}
|
|
578
|
+
function getSafeAreaInsets() {
|
|
579
|
+
if (typeof window === "undefined") {
|
|
580
|
+
return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
581
|
+
}
|
|
582
|
+
const computedStyle = window.getComputedStyle(document.documentElement);
|
|
583
|
+
const parseValue = (value) => {
|
|
584
|
+
const num = parseFloat(value);
|
|
585
|
+
return isNaN(num) ? 0 : num;
|
|
586
|
+
};
|
|
587
|
+
return {
|
|
588
|
+
top: parseValue(computedStyle.getPropertyValue("--safe-area-inset-top")),
|
|
589
|
+
right: parseValue(computedStyle.getPropertyValue("--safe-area-inset-right")),
|
|
590
|
+
bottom: parseValue(computedStyle.getPropertyValue("--safe-area-inset-bottom")),
|
|
591
|
+
left: parseValue(computedStyle.getPropertyValue("--safe-area-inset-left"))
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// src/unified/react/use-host-styles.ts
|
|
596
|
+
function useHostStyles(context, options = {}) {
|
|
597
|
+
const {
|
|
598
|
+
applyTheme = true,
|
|
599
|
+
applyVariables = true,
|
|
600
|
+
applyFonts = true,
|
|
601
|
+
applySafeArea = true,
|
|
602
|
+
clearOnUnmount = true
|
|
603
|
+
} = options;
|
|
604
|
+
const appliedRef = useRef(false);
|
|
605
|
+
useEffect(() => {
|
|
606
|
+
if (!context) return;
|
|
607
|
+
if (applyTheme && context.theme) {
|
|
608
|
+
applyDocumentTheme(context.theme);
|
|
609
|
+
}
|
|
610
|
+
if (applyVariables && context.styles?.variables) {
|
|
611
|
+
applyHostStyleVariables(context.styles.variables);
|
|
612
|
+
}
|
|
613
|
+
if (applyFonts && context.styles?.css?.fonts) {
|
|
614
|
+
applyHostFonts(context.styles.css.fonts);
|
|
615
|
+
}
|
|
616
|
+
if (applySafeArea && context.safeAreaInsets) {
|
|
617
|
+
applySafeAreaInsets(context.safeAreaInsets);
|
|
618
|
+
}
|
|
619
|
+
appliedRef.current = true;
|
|
620
|
+
}, [context, applyTheme, applyVariables, applyFonts, applySafeArea]);
|
|
621
|
+
useEffect(() => {
|
|
622
|
+
return () => {
|
|
623
|
+
if (clearOnUnmount && appliedRef.current) {
|
|
624
|
+
clearHostStyles();
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
}, [clearOnUnmount]);
|
|
628
|
+
}
|
|
629
|
+
function useHostTheme(context) {
|
|
630
|
+
useEffect(() => {
|
|
631
|
+
if (context?.theme) {
|
|
632
|
+
applyDocumentTheme(context.theme);
|
|
633
|
+
}
|
|
634
|
+
}, [context?.theme]);
|
|
635
|
+
}
|
|
636
|
+
function useHostVariables(context) {
|
|
637
|
+
useEffect(() => {
|
|
638
|
+
if (context?.styles?.variables) {
|
|
639
|
+
applyHostStyleVariables(context.styles.variables);
|
|
640
|
+
}
|
|
641
|
+
}, [context?.styles?.variables]);
|
|
642
|
+
}
|
|
643
|
+
function useHostFontsHook(context) {
|
|
644
|
+
useEffect(() => {
|
|
645
|
+
if (context?.styles?.css?.fonts) {
|
|
646
|
+
applyHostFonts(context.styles.css.fonts);
|
|
647
|
+
}
|
|
648
|
+
}, [context?.styles?.css?.fonts]);
|
|
649
|
+
}
|
|
650
|
+
function useDocumentTheme() {
|
|
651
|
+
return useSyncExternalStore(
|
|
652
|
+
subscribeToTheme,
|
|
653
|
+
getDocumentTheme,
|
|
654
|
+
getServerTheme
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
function subscribeToTheme(callback) {
|
|
658
|
+
if (typeof window === "undefined") {
|
|
659
|
+
return () => {
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
663
|
+
mediaQuery.addEventListener("change", callback);
|
|
664
|
+
const observer = new MutationObserver(callback);
|
|
665
|
+
observer.observe(document.documentElement, {
|
|
666
|
+
attributes: true,
|
|
667
|
+
attributeFilter: ["class", "data-theme"]
|
|
668
|
+
});
|
|
669
|
+
return () => {
|
|
670
|
+
mediaQuery.removeEventListener("change", callback);
|
|
671
|
+
observer.disconnect();
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
function getServerTheme() {
|
|
675
|
+
return "light";
|
|
676
|
+
}
|
|
677
|
+
function useDocumentThemeState() {
|
|
678
|
+
const [theme, setTheme] = useState(() => {
|
|
679
|
+
if (typeof window === "undefined") return "light";
|
|
680
|
+
return getDocumentTheme();
|
|
681
|
+
});
|
|
682
|
+
useEffect(() => {
|
|
683
|
+
const { disconnect } = observeDocumentTheme((newTheme) => {
|
|
684
|
+
setTheme(newTheme);
|
|
685
|
+
});
|
|
686
|
+
return disconnect;
|
|
687
|
+
}, []);
|
|
688
|
+
return theme;
|
|
689
|
+
}
|
|
690
|
+
function useSystemColorScheme() {
|
|
691
|
+
return useSyncExternalStore(
|
|
692
|
+
subscribeToSystemColorScheme,
|
|
693
|
+
getSystemColorScheme,
|
|
694
|
+
getServerTheme
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
function subscribeToSystemColorScheme(callback) {
|
|
698
|
+
if (typeof window === "undefined") {
|
|
699
|
+
return () => {
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
703
|
+
mediaQuery.addEventListener("change", callback);
|
|
704
|
+
return () => {
|
|
705
|
+
mediaQuery.removeEventListener("change", callback);
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
function getSystemColorScheme() {
|
|
709
|
+
if (typeof window === "undefined") return "light";
|
|
710
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
export { View, applyDocumentTheme, applyHostContext, applyHostFonts, applyHostStyleVariables, applySafeAreaInsets, clearHostStyles, createView, getComputedStyleVariables, getDocumentTheme, getSafeAreaInsets, observeDocumentTheme, useDocumentTheme, useDocumentThemeState, useHostFontsHook, useHostStyles, useHostTheme, useHostVariables, useSystemColorScheme, useView };
|
|
714
|
+
//# sourceMappingURL=chunk-TLBYYZHP.js.map
|
|
715
|
+
//# sourceMappingURL=chunk-TLBYYZHP.js.map
|