@webskill/chatbot 0.13.0 → 0.15.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/dist/chatbot.css CHANGED
@@ -349,6 +349,12 @@
349
349
  max-width: 96rem;
350
350
  }
351
351
  }
352
+ .m-1 {
353
+ margin: var(--spacing);
354
+ }
355
+ .m-2 {
356
+ margin: calc(var(--spacing) * 2);
357
+ }
352
358
  .m-3 {
353
359
  margin: calc(var(--spacing) * 3);
354
360
  }
@@ -1068,6 +1074,9 @@
1068
1074
  .fill-ink {
1069
1075
  fill: var(--foreground);
1070
1076
  }
1077
+ .object-contain {
1078
+ object-fit: contain;
1079
+ }
1071
1080
  .object-cover {
1072
1081
  object-fit: cover;
1073
1082
  }
package/dist/index.d.ts CHANGED
@@ -1492,6 +1492,8 @@ declare const chatbotDictionary: {
1492
1492
  'interaction.pageAction.select': string;
1493
1493
  'interaction.pageAction.set': string;
1494
1494
  'interaction.pageAction.attach': string;
1495
+ 'interaction.pageAction.scroll.down': string;
1496
+ 'interaction.pageAction.scroll.up': string;
1495
1497
  'interaction.pageAction.back': string;
1496
1498
  'interaction.pageAction.role.click': string;
1497
1499
  'interaction.pageAction.role.fill': string;
@@ -1499,6 +1501,7 @@ declare const chatbotDictionary: {
1499
1501
  'interaction.pageAction.role.select': string;
1500
1502
  'interaction.pageAction.role.set': string;
1501
1503
  'interaction.pageAction.role.attach': string;
1504
+ 'interaction.pageAction.role.scroll': string;
1502
1505
  'interaction.pageAction.role.back': string;
1503
1506
  'interaction.pageAction.remember': string;
1504
1507
  'interaction.pageAction.frame': string;
@@ -1507,6 +1510,13 @@ declare const chatbotDictionary: {
1507
1510
  'interaction.downloadedFile.read': string;
1508
1511
  'interaction.downloadedFile.file': string;
1509
1512
  'interaction.downloadedFile.remember': string;
1513
+ 'interaction.uploadFile.read': string;
1514
+ 'interaction.uploadFile.file': string;
1515
+ 'interaction.sandbox.readReference': string;
1516
+ 'interaction.sandbox.readAsset': string;
1517
+ 'interaction.sandbox.fetchData': string;
1518
+ 'interaction.sandbox.writeArtifact': string;
1519
+ 'interaction.sandbox.confirm': string;
1510
1520
  'interaction.traceEvidence.title': string;
1511
1521
  'interaction.traceEvidence.columnDraft': string;
1512
1522
  'interaction.traceEvidence.columnTrace': string;
@@ -1580,6 +1590,20 @@ declare const chatbotDictionary: {
1580
1590
  'composer.dictate.recording': string;
1581
1591
  'composer.dictate.api-missing': string;
1582
1592
  'composer.dictate.insecure-context': string;
1593
+ 'composer.dictate.denied': string;
1594
+ 'composer.dictate.denied.handoff': string;
1595
+ 'composer.dictate.failed': string;
1596
+ 'composer.camera': string;
1597
+ 'composer.camera.title': string;
1598
+ 'composer.camera.description': string;
1599
+ 'composer.camera.preview': string;
1600
+ 'composer.camera.shutter': string;
1601
+ 'composer.camera.noStorage': string;
1602
+ 'composer.camera.api-missing': string;
1603
+ 'composer.camera.insecure-context': string;
1604
+ 'composer.camera.denied': string;
1605
+ 'composer.camera.denied.handoff': string;
1606
+ 'composer.camera.failed': string;
1583
1607
  'settings.title': string;
1584
1608
  'settings.group.general': string;
1585
1609
  'settings.group.models': string;
@@ -1858,6 +1882,8 @@ declare const chatbotDictionary: {
1858
1882
  'interaction.pageAction.select': string;
1859
1883
  'interaction.pageAction.set': string;
1860
1884
  'interaction.pageAction.attach': string;
1885
+ 'interaction.pageAction.scroll.down': string;
1886
+ 'interaction.pageAction.scroll.up': string;
1861
1887
  'interaction.pageAction.back': string;
1862
1888
  'interaction.pageAction.role.click': string;
1863
1889
  'interaction.pageAction.role.fill': string;
@@ -1865,6 +1891,7 @@ declare const chatbotDictionary: {
1865
1891
  'interaction.pageAction.role.select': string;
1866
1892
  'interaction.pageAction.role.set': string;
1867
1893
  'interaction.pageAction.role.attach': string;
1894
+ 'interaction.pageAction.role.scroll': string;
1868
1895
  'interaction.pageAction.role.back': string;
1869
1896
  'interaction.pageAction.remember': string;
1870
1897
  'interaction.pageAction.frame': string;
@@ -1873,6 +1900,13 @@ declare const chatbotDictionary: {
1873
1900
  'interaction.downloadedFile.read': string;
1874
1901
  'interaction.downloadedFile.file': string;
1875
1902
  'interaction.downloadedFile.remember': string;
1903
+ 'interaction.uploadFile.read': string;
1904
+ 'interaction.uploadFile.file': string;
1905
+ 'interaction.sandbox.readReference': string;
1906
+ 'interaction.sandbox.readAsset': string;
1907
+ 'interaction.sandbox.fetchData': string;
1908
+ 'interaction.sandbox.writeArtifact': string;
1909
+ 'interaction.sandbox.confirm': string;
1876
1910
  'interaction.traceEvidence.title': string;
1877
1911
  'interaction.traceEvidence.columnDraft': string;
1878
1912
  'interaction.traceEvidence.columnTrace': string;
@@ -1946,6 +1980,20 @@ declare const chatbotDictionary: {
1946
1980
  'composer.dictate.recording': string;
1947
1981
  'composer.dictate.api-missing': string;
1948
1982
  'composer.dictate.insecure-context': string;
1983
+ 'composer.dictate.denied': string;
1984
+ 'composer.dictate.denied.handoff': string;
1985
+ 'composer.dictate.failed': string;
1986
+ 'composer.camera': string;
1987
+ 'composer.camera.title': string;
1988
+ 'composer.camera.description': string;
1989
+ 'composer.camera.preview': string;
1990
+ 'composer.camera.shutter': string;
1991
+ 'composer.camera.noStorage': string;
1992
+ 'composer.camera.api-missing': string;
1993
+ 'composer.camera.insecure-context': string;
1994
+ 'composer.camera.denied': string;
1995
+ 'composer.camera.denied.handoff': string;
1996
+ 'composer.camera.failed': string;
1949
1997
  'settings.title': string;
1950
1998
  'settings.group.general': string;
1951
1999
  'settings.group.models': string;
@@ -2227,6 +2275,8 @@ declare const useT: () => TranslateFn<{
2227
2275
  'interaction.pageAction.select': string;
2228
2276
  'interaction.pageAction.set': string;
2229
2277
  'interaction.pageAction.attach': string;
2278
+ 'interaction.pageAction.scroll.down': string;
2279
+ 'interaction.pageAction.scroll.up': string;
2230
2280
  'interaction.pageAction.back': string;
2231
2281
  'interaction.pageAction.role.click': string;
2232
2282
  'interaction.pageAction.role.fill': string;
@@ -2234,6 +2284,7 @@ declare const useT: () => TranslateFn<{
2234
2284
  'interaction.pageAction.role.select': string;
2235
2285
  'interaction.pageAction.role.set': string;
2236
2286
  'interaction.pageAction.role.attach': string;
2287
+ 'interaction.pageAction.role.scroll': string;
2237
2288
  'interaction.pageAction.role.back': string;
2238
2289
  'interaction.pageAction.remember': string;
2239
2290
  'interaction.pageAction.frame': string;
@@ -2242,6 +2293,13 @@ declare const useT: () => TranslateFn<{
2242
2293
  'interaction.downloadedFile.read': string;
2243
2294
  'interaction.downloadedFile.file': string;
2244
2295
  'interaction.downloadedFile.remember': string;
2296
+ 'interaction.uploadFile.read': string;
2297
+ 'interaction.uploadFile.file': string;
2298
+ 'interaction.sandbox.readReference': string;
2299
+ 'interaction.sandbox.readAsset': string;
2300
+ 'interaction.sandbox.fetchData': string;
2301
+ 'interaction.sandbox.writeArtifact': string;
2302
+ 'interaction.sandbox.confirm': string;
2245
2303
  'interaction.traceEvidence.title': string;
2246
2304
  'interaction.traceEvidence.columnDraft': string;
2247
2305
  'interaction.traceEvidence.columnTrace': string;
@@ -2315,6 +2373,20 @@ declare const useT: () => TranslateFn<{
2315
2373
  'composer.dictate.recording': string;
2316
2374
  'composer.dictate.api-missing': string;
2317
2375
  'composer.dictate.insecure-context': string;
2376
+ 'composer.dictate.denied': string;
2377
+ 'composer.dictate.denied.handoff': string;
2378
+ 'composer.dictate.failed': string;
2379
+ 'composer.camera': string;
2380
+ 'composer.camera.title': string;
2381
+ 'composer.camera.description': string;
2382
+ 'composer.camera.preview': string;
2383
+ 'composer.camera.shutter': string;
2384
+ 'composer.camera.noStorage': string;
2385
+ 'composer.camera.api-missing': string;
2386
+ 'composer.camera.insecure-context': string;
2387
+ 'composer.camera.denied': string;
2388
+ 'composer.camera.denied.handoff': string;
2389
+ 'composer.camera.failed': string;
2318
2390
  'settings.title': string;
2319
2391
  'settings.group.general': string;
2320
2392
  'settings.group.models': string;
@@ -2593,6 +2665,8 @@ declare const useT: () => TranslateFn<{
2593
2665
  'interaction.pageAction.select': string;
2594
2666
  'interaction.pageAction.set': string;
2595
2667
  'interaction.pageAction.attach': string;
2668
+ 'interaction.pageAction.scroll.down': string;
2669
+ 'interaction.pageAction.scroll.up': string;
2596
2670
  'interaction.pageAction.back': string;
2597
2671
  'interaction.pageAction.role.click': string;
2598
2672
  'interaction.pageAction.role.fill': string;
@@ -2600,6 +2674,7 @@ declare const useT: () => TranslateFn<{
2600
2674
  'interaction.pageAction.role.select': string;
2601
2675
  'interaction.pageAction.role.set': string;
2602
2676
  'interaction.pageAction.role.attach': string;
2677
+ 'interaction.pageAction.role.scroll': string;
2603
2678
  'interaction.pageAction.role.back': string;
2604
2679
  'interaction.pageAction.remember': string;
2605
2680
  'interaction.pageAction.frame': string;
@@ -2608,6 +2683,13 @@ declare const useT: () => TranslateFn<{
2608
2683
  'interaction.downloadedFile.read': string;
2609
2684
  'interaction.downloadedFile.file': string;
2610
2685
  'interaction.downloadedFile.remember': string;
2686
+ 'interaction.uploadFile.read': string;
2687
+ 'interaction.uploadFile.file': string;
2688
+ 'interaction.sandbox.readReference': string;
2689
+ 'interaction.sandbox.readAsset': string;
2690
+ 'interaction.sandbox.fetchData': string;
2691
+ 'interaction.sandbox.writeArtifact': string;
2692
+ 'interaction.sandbox.confirm': string;
2611
2693
  'interaction.traceEvidence.title': string;
2612
2694
  'interaction.traceEvidence.columnDraft': string;
2613
2695
  'interaction.traceEvidence.columnTrace': string;
@@ -2681,6 +2763,20 @@ declare const useT: () => TranslateFn<{
2681
2763
  'composer.dictate.recording': string;
2682
2764
  'composer.dictate.api-missing': string;
2683
2765
  'composer.dictate.insecure-context': string;
2766
+ 'composer.dictate.denied': string;
2767
+ 'composer.dictate.denied.handoff': string;
2768
+ 'composer.dictate.failed': string;
2769
+ 'composer.camera': string;
2770
+ 'composer.camera.title': string;
2771
+ 'composer.camera.description': string;
2772
+ 'composer.camera.preview': string;
2773
+ 'composer.camera.shutter': string;
2774
+ 'composer.camera.noStorage': string;
2775
+ 'composer.camera.api-missing': string;
2776
+ 'composer.camera.insecure-context': string;
2777
+ 'composer.camera.denied': string;
2778
+ 'composer.camera.denied.handoff': string;
2779
+ 'composer.camera.failed': string;
2684
2780
  'settings.title': string;
2685
2781
  'settings.group.general': string;
2686
2782
  'settings.group.models': string;
@@ -2874,6 +2970,18 @@ interface ChatbotProps {
2874
2970
  renderer?: RendererKind;
2875
2971
  /** 语音识别语言(BCP-47);缺省跟随界面语言(FR-7.7) */
2876
2972
  dictationLang?: string;
2973
+ /**
2974
+ * 麦克风授权被拒时的宿主出口(0.17.0 FR-10.1)。
2975
+ * 侧栏 / popup 这类页面没地方错权限气泡,只有宿主知道该把用户带到哪儿去。
2976
+ * 不接就只给一句「去浏览器权限设置里允许」的提示,行为与以往一致。
2977
+ */
2978
+ onDictationPermissionDenied?(): void;
2979
+ /**
2980
+ * 摄像头授权被拒时的宿主出口(0.17.0 FR-14.7)。
2981
+ * 机制与 `onDictationPermissionDenied` 完全一致:侧栏 / popup 弹不出浏览器的授权气泡。
2982
+ * 不接就只给一句「去浏览器权限设置里允许」的提示。
2983
+ */
2984
+ onCameraPermissionDenied?(): void;
2877
2985
  /** Native custom surface 的显式 renderer/schema allowlist。 */
2878
2986
  surfaceRegistry?: SurfaceRegistry;
2879
2987
  /** 布局档(默认 'auto':按容器尺寸在 fullscreen / embedded / mobile 之间判定) */
@@ -2912,7 +3020,7 @@ interface ChatbotProps {
2912
3020
  * presentation shell and accessible thread primitives.
2913
3021
  * @stable
2914
3022
  */
2915
- declare function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, renderer: rendererProp, dictationLang: dictationLangProp, surfaceRegistry, layout, sessionList, onOpenSettings, docked, onDock, onClose, onEngineReady }: ChatbotProps): import("react").JSX.Element;
3023
+ declare function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, renderer: rendererProp, dictationLang: dictationLangProp, onDictationPermissionDenied, onCameraPermissionDenied, surfaceRegistry, layout, sessionList, onOpenSettings, docked, onDock, onClose, onEngineReady }: ChatbotProps): import("react").JSX.Element;
2916
3024
  //#endregion
2917
3025
  //#region src/react/A2uiSurfaceHost.d.ts
2918
3026
  /** @experimental */
@@ -3096,6 +3204,6 @@ declare function ErrorCard({ code, message, loopLimits, onOpenSettings, onDismis
3096
3204
  * Version of the published `@webskill/chatbot` package, injected at build time.
3097
3205
  * @stable
3098
3206
  */
3099
- declare const CHATBOT_VERSION = "0.13.0";
3207
+ declare const CHATBOT_VERSION = "0.15.0";
3100
3208
  //#endregion
3101
3209
  export { A2uiSurfaceHost, type A2uiSurfaceHostProps, A2uiSurfaceSnapshotHost, type A2uiSurfaceSnapshotHostProps, type AppearanceChange, CHATBOT_VERSION, type ChatAttachmentInput, type ChatAttachmentMeta, type ChatContentPart, ChatEngine, type ChatEngineOptions, type ChatEvent, type ChatInteractionRecord, type ChatLayout, type ChatMessage, type ChatRunPhase, type ChatSessionListResult, type ChatSessionMeta, type ChatTheme, type ChatThinking, type ChatTokenUsage, type ChatToolCall, Chatbot, type ChatbotConfig, type ChatbotGovernancePorts, type ChatbotHostAdapter, type ChatbotProps, CompositeUiBridge, type CompositeUiBridgeDeps, DEFAULT_RENDERER_CAPABILITIES, DEFAULT_RUNTIME_CONFIG_STORAGE_KEY, type DownloadableFile, ErrorCard, type ErrorCardProps, InteractionCard, type InteractionCardProps, InterruptedBanner, type InterruptedBannerProps, type Locale, type LocalizedText, OpenUiSurfaceHost, type OpenUiSurfaceHostProps, OpenUiSurfaceSnapshotHost, type OpenUiSurfaceSnapshotHostProps, type QuickPrompt, type QuickPromptIconName, type RendererCapability, type RendererKind, type ResolvedChatLayout, ResultBlockList, type ResultBlockListProps, ResultBlocksPro, type ResultBlocksProProps, type RunSnapshot, type RuntimeAppearanceConfig, type RuntimeConfig, type RuntimeConfigStore, type RuntimeQuickPrompt, type RuntimeRendererId, type SandboxExecutorDeps, type SettingsSectionId, SkillBadges, SpecInteraction, type SpecInteractionProps, VercelPayloadPreview, type VercelPayloadPreviewProps, VercelSurfaceHost, type VercelSurfaceHostProps, VercelSurfaceSnapshotHost, type VercelSurfaceSnapshotHostProps, chatbotDictionary, configureA2uiMarkdown, createLocalStorageRuntimeConfigStore, createMemoryRuntimeConfigStore, isLlmEntryUsable, pickUsableLlmEntry, probeA2uiAvailability, probeOpenUiAvailability, sandboxExecutorDeps, useT };
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { t as json } from "./json-BOcU6nei.js";
7
7
  import { t as typescript } from "./typescript-DAau_Qyl.js";
8
8
  import { t as jsx$1 } from "./jsx-D2Jv0wvt.js";
9
9
  import { BEHAVIOR_RECORDS_KEY, DEFAULT_LOOP_LIMITS, DEFAULT_USER_PROFILE_LIMITS, EventBus, FsArtifactStore, FsMemoryStore, FsRunSnapshotStore, FsRunTraceStore, FsSessionStore, FsToolStepStore, FullDisclosureRouter, SUPPORTED_DOCUMENT_MIME, SerializingMemoryStore, SkillDiscovery, USER_PROFILE_KEY, WebSkillError, WebSkillRuntime, applyUserProfileImport, assertRemoteUrlAllowed, assertSafePathSegment, atomicWriteText, diffUserProfile, exportUserProfile, formatAttachmentText, isUnsupportedRunSnapshot, messageOf, readBehaviorRecords, readUserProfile, refineUserProfile, resolveInsideRoot, sanitizeUntrustedLine, summarizeRunUsage, summarizeToolCalls, textParts } from "@webskill/sdk";
10
- import { BrowserWorkerScriptExecutor, checkDictationAvailability, compressImageToBudget, createEncryptedMemoryStore, createLlmClient, deleteMemoryEncryptionKey, openMemoryEncryptionKey, probeChromeBuiltinAvailability, startDictation } from "@webskill/sdk/browser";
10
+ import { BrowserWorkerScriptExecutor, capturePhoto, checkCameraAvailability, checkDictationAvailability, compressImageToBudget, createEncryptedMemoryStore, createLlmClient, deleteMemoryEncryptionKey, openCamera, openMemoryEncryptionKey, probeChromeBuiltinAvailability, startDictation } from "@webskill/sdk/browser";
11
11
  import { JsonRenderSpecSurface, NativeSpecSurface, OpenUiSpecSurface, ReactBridgeState, SpecInteractionChannel, SurfaceFormTextsProvider, UiSurfaceSnapshotList, probeJsonRenderAvailability } from "@webskill/sdk/ui-react";
12
12
  import { A2UI_SPEC_FORM_PATH, collectFormScopes, createUiCatalogToolSource, fromA2uiSpecAction, fromA2uiSurfaceAction, interactionToFormModel, interactionToUiSpec, loadOpenUiPeers, loadWebSkillLitCatalog, renderMiniChart, shapeInteractionValue, toA2uiSpecMessages, toA2uiSurfaceAction, toVercelToolInvocation, uiCatalog } from "@webskill/sdk/ui";
13
13
  import { DownloadedFilePolicy, PAGE_ACTION_KINDS, TodoStore, createDelegationToolSource, createDownloadedFileToolSource, createPageActionToolSource, createPagePerceptionToolSource, createSkillGenerationToolSource, createTabWorksetToolSource, createTodoToolSource, withDelegationOrigin } from "@webskill/sdk/agent";
@@ -254,7 +254,7 @@ const createLucideIcon = (iconName, iconNode) => {
254
254
  * This source code is licensed under the ISC license.
255
255
  * See the LICENSE file in the root directory of this source tree.
256
256
  */
257
- const __iconNode$49 = [
257
+ const __iconNode$50 = [
258
258
  ["rect", {
259
259
  x: "2",
260
260
  y: "4",
@@ -276,7 +276,7 @@ const __iconNode$49 = [
276
276
  key: "1svtjw"
277
277
  }]
278
278
  ];
279
- const AppWindow = createLucideIcon("app-window", __iconNode$49);
279
+ const AppWindow = createLucideIcon("app-window", __iconNode$50);
280
280
 
281
281
  //#endregion
282
282
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/archive-restore.mjs
@@ -286,7 +286,7 @@ const AppWindow = createLucideIcon("app-window", __iconNode$49);
286
286
  * This source code is licensed under the ISC license.
287
287
  * See the LICENSE file in the root directory of this source tree.
288
288
  */
289
- const __iconNode$48 = [
289
+ const __iconNode$49 = [
290
290
  ["rect", {
291
291
  width: "20",
292
292
  height: "5",
@@ -312,7 +312,7 @@ const __iconNode$48 = [
312
312
  key: "192myk"
313
313
  }]
314
314
  ];
315
- const ArchiveRestore = createLucideIcon("archive-restore", __iconNode$48);
315
+ const ArchiveRestore = createLucideIcon("archive-restore", __iconNode$49);
316
316
 
317
317
  //#endregion
318
318
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/archive.mjs
@@ -322,7 +322,7 @@ const ArchiveRestore = createLucideIcon("archive-restore", __iconNode$48);
322
322
  * This source code is licensed under the ISC license.
323
323
  * See the LICENSE file in the root directory of this source tree.
324
324
  */
325
- const __iconNode$47 = [
325
+ const __iconNode$48 = [
326
326
  ["rect", {
327
327
  width: "20",
328
328
  height: "5",
@@ -340,7 +340,7 @@ const __iconNode$47 = [
340
340
  key: "a56b0p"
341
341
  }]
342
342
  ];
343
- const Archive = createLucideIcon("archive", __iconNode$47);
343
+ const Archive = createLucideIcon("archive", __iconNode$48);
344
344
 
345
345
  //#endregion
346
346
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/arrow-up.mjs
@@ -350,14 +350,14 @@ const Archive = createLucideIcon("archive", __iconNode$47);
350
350
  * This source code is licensed under the ISC license.
351
351
  * See the LICENSE file in the root directory of this source tree.
352
352
  */
353
- const __iconNode$46 = [["path", {
353
+ const __iconNode$47 = [["path", {
354
354
  d: "m5 12 7-7 7 7",
355
355
  key: "hav0vg"
356
356
  }], ["path", {
357
357
  d: "M12 19V5",
358
358
  key: "x0mq9r"
359
359
  }]];
360
- const ArrowUp = createLucideIcon("arrow-up", __iconNode$46);
360
+ const ArrowUp = createLucideIcon("arrow-up", __iconNode$47);
361
361
 
362
362
  //#endregion
363
363
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/bug.mjs
@@ -367,7 +367,7 @@ const ArrowUp = createLucideIcon("arrow-up", __iconNode$46);
367
367
  * This source code is licensed under the ISC license.
368
368
  * See the LICENSE file in the root directory of this source tree.
369
369
  */
370
- const __iconNode$45 = [
370
+ const __iconNode$46 = [
371
371
  ["path", {
372
372
  d: "M12 20v-9",
373
373
  key: "1qisl0"
@@ -413,7 +413,26 @@ const __iconNode$45 = [
413
413
  key: "1vgav8"
414
414
  }]
415
415
  ];
416
- const Bug = createLucideIcon("bug", __iconNode$45);
416
+ const Bug = createLucideIcon("bug", __iconNode$46);
417
+
418
+ //#endregion
419
+ //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/camera.mjs
420
+ /**
421
+ * @license lucide-react v1.27.0 - ISC
422
+ *
423
+ * This source code is licensed under the ISC license.
424
+ * See the LICENSE file in the root directory of this source tree.
425
+ */
426
+ const __iconNode$45 = [["path", {
427
+ d: "M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z",
428
+ key: "18u6gg"
429
+ }], ["circle", {
430
+ cx: "12",
431
+ cy: "13",
432
+ r: "3",
433
+ key: "1vg3eu"
434
+ }]];
435
+ const Camera = createLucideIcon("camera", __iconNode$45);
417
436
 
418
437
  //#endregion
419
438
  //#region ../../node_modules/.pnpm/lucide-react@1.27.0_react@19.2.8/node_modules/lucide-react/dist/esm/icons/chart-column.mjs
@@ -30291,6 +30310,12 @@ const markdownComponents = {
30291
30310
  /**
30292
30311
  * 流式 Markdown:消费当前 text part 的增量文本,边流边成形(标题/列表/表格/代码块),
30293
30312
  * 终态与流式期是同一个组件、同一份数据,因此 run 结束时不重排。
30313
+ *
30314
+ * `smooth={false}` 是硬性要求,不是口味问题:assistant-ui 以 partIndex 作 React key
30315
+ * (`MessagePartsGrouped`),而运行细节的 part 排在正文之前,每来一次工具调用正文就整体后移一位、
30316
+ * 被卸载重挂。平滑揭示在重挂时从空串重新开始,于是同一段正文会被完整地重打一遍——
30317
+ * 表现为「正文下半段删掉又补回来、高度忽高忽低」。模型本身已按 token 粒度流式返回,
30318
+ * 再叠一层揭示动画只会把结构性重挂放大成可见抖动。守卫见 `e2e-extension/liveBodyMonotonic.spec.ts`。
30294
30319
  */
30295
30320
  const MarkdownText = memo(function MarkdownText({ className }) {
30296
30321
  const [dark, themeRef] = useDarkAncestor();
@@ -30302,6 +30327,7 @@ const MarkdownText = memo(function MarkdownText({ className }) {
30302
30327
  remarkPlugins: [remarkGfm],
30303
30328
  className: cn("aui-md", className),
30304
30329
  components,
30330
+ smooth: false,
30305
30331
  defer: true
30306
30332
  })
30307
30333
  });
@@ -31683,6 +31709,8 @@ const chatbotDictionary = defineDictionary({
31683
31709
  "interaction.pageAction.select": "Allow the assistant to select an option in “{target}”?",
31684
31710
  "interaction.pageAction.set": "Allow the assistant to toggle “{target}”?",
31685
31711
  "interaction.pageAction.attach": "Allow the assistant to attach a file to “{target}”?",
31712
+ "interaction.pageAction.scroll.down": "Allow the assistant to scroll down the area around “{target}”?",
31713
+ "interaction.pageAction.scroll.up": "Allow the assistant to scroll up the area around “{target}”?",
31686
31714
  "interaction.pageAction.back": "Allow the assistant to go back to the previous page on “{target}”?",
31687
31715
  "interaction.pageAction.role.click": "this control",
31688
31716
  "interaction.pageAction.role.fill": "this field",
@@ -31690,6 +31718,7 @@ const chatbotDictionary = defineDictionary({
31690
31718
  "interaction.pageAction.role.select": "this list",
31691
31719
  "interaction.pageAction.role.set": "this switch",
31692
31720
  "interaction.pageAction.role.attach": "this upload field",
31721
+ "interaction.pageAction.role.scroll": "this area",
31693
31722
  "interaction.pageAction.role.back": "this page",
31694
31723
  "interaction.pageAction.remember": "Don’t ask again for this kind of action",
31695
31724
  "interaction.pageAction.frame": "The target is in the embedded frame \"{frame}\".",
@@ -31698,6 +31727,13 @@ const chatbotDictionary = defineDictionary({
31698
31727
  "interaction.downloadedFile.read": "Allow the assistant to read this file from your downloads?",
31699
31728
  "interaction.downloadedFile.file": "{name} · {size}",
31700
31729
  "interaction.downloadedFile.remember": "Don’t ask again for this kind of access",
31730
+ "interaction.uploadFile.read": "Allow the skill “{skill}” to read the file you uploaded?",
31731
+ "interaction.uploadFile.file": "{name} · {type} · {size}",
31732
+ "interaction.sandbox.readReference": "Allow the skill “{skill}” to read the reference file “{path}”?",
31733
+ "interaction.sandbox.readAsset": "Allow the skill “{skill}” to read the bundled asset “{path}”?",
31734
+ "interaction.sandbox.fetchData": "Allow the skill “{skill}” to fetch the data source “{source}”?",
31735
+ "interaction.sandbox.writeArtifact": "Allow the skill “{skill}” to write the artifact “{path}”?",
31736
+ "interaction.sandbox.confirm": "The skill “{skill}” asks for confirmation:",
31701
31737
  "interaction.traceEvidence.title": "Steps this skill claims",
31702
31738
  "interaction.traceEvidence.columnDraft": "In the skill",
31703
31739
  "interaction.traceEvidence.columnTrace": "Actually run in this session",
@@ -31771,6 +31807,20 @@ const chatbotDictionary = defineDictionary({
31771
31807
  "composer.dictate.recording": "Recording",
31772
31808
  "composer.dictate.api-missing": "This browser does not support speech recognition.",
31773
31809
  "composer.dictate.insecure-context": "Voice input needs a secure connection (https or localhost).",
31810
+ "composer.dictate.denied": "The browser denied microphone access. Allow it in the site permission settings, then try again.",
31811
+ "composer.dictate.denied.handoff": "Microphone access needs to be granted once in a regular tab. Finish it there, then come back and try again.",
31812
+ "composer.dictate.failed": "Voice input stopped unexpectedly. Try again, or type your message.",
31813
+ "composer.camera": "Take a photo",
31814
+ "composer.camera.title": "Take a photo",
31815
+ "composer.camera.description": "The photo is added to the composer as an attachment. It is not sent until you send the message.",
31816
+ "composer.camera.preview": "Camera preview",
31817
+ "composer.camera.shutter": "Capture",
31818
+ "composer.camera.noStorage": "attachments are unavailable in this host",
31819
+ "composer.camera.api-missing": "This browser does not support camera capture.",
31820
+ "composer.camera.insecure-context": "The camera needs a secure connection (https or localhost).",
31821
+ "composer.camera.denied": "The browser denied camera access. Allow it in the site permission settings, then try again.",
31822
+ "composer.camera.denied.handoff": "Camera access needs to be granted once in a regular tab. Finish it there, then come back and try again.",
31823
+ "composer.camera.failed": "The photo could not be taken. Try again, or attach an image file instead.",
31774
31824
  "settings.title": "Settings",
31775
31825
  "settings.group.general": "General",
31776
31826
  "settings.group.models": "Models",
@@ -32049,6 +32099,8 @@ const chatbotDictionary = defineDictionary({
32049
32099
  "interaction.pageAction.select": "允许助手在「{target}」中选择选项吗?",
32050
32100
  "interaction.pageAction.set": "允许助手切换「{target}」吗?",
32051
32101
  "interaction.pageAction.attach": "允许助手向「{target}」附加文件吗?",
32102
+ "interaction.pageAction.scroll.down": "允许助手在「{target}」所在区域向下滚动吗?",
32103
+ "interaction.pageAction.scroll.up": "允许助手在「{target}」所在区域向上滚动吗?",
32052
32104
  "interaction.pageAction.back": "允许助手在「{target}」上返回上一页吗?",
32053
32105
  "interaction.pageAction.role.click": "这个控件",
32054
32106
  "interaction.pageAction.role.fill": "这个输入框",
@@ -32056,6 +32108,7 @@ const chatbotDictionary = defineDictionary({
32056
32108
  "interaction.pageAction.role.select": "这个选择框",
32057
32109
  "interaction.pageAction.role.set": "这个开关",
32058
32110
  "interaction.pageAction.role.attach": "这个上传控件",
32111
+ "interaction.pageAction.role.scroll": "这一块",
32059
32112
  "interaction.pageAction.role.back": "这个页面",
32060
32113
  "interaction.pageAction.remember": "以后不再询问此类操作",
32061
32114
  "interaction.pageAction.frame": "目标位于嵌入帧「{frame}」中。",
@@ -32064,6 +32117,13 @@ const chatbotDictionary = defineDictionary({
32064
32117
  "interaction.downloadedFile.read": "允许助手读取你下载目录里的这份文件吗?",
32065
32118
  "interaction.downloadedFile.file": "{name} · {size}",
32066
32119
  "interaction.downloadedFile.remember": "以后不再询问此类访问",
32120
+ "interaction.uploadFile.read": "允许技能「{skill}」读取你上传的这份文件吗?",
32121
+ "interaction.uploadFile.file": "{name} · {type} · {size}",
32122
+ "interaction.sandbox.readReference": "允许技能「{skill}」读取参考资料「{path}」吗?",
32123
+ "interaction.sandbox.readAsset": "允许技能「{skill}」读取随附文件「{path}」吗?",
32124
+ "interaction.sandbox.fetchData": "允许技能「{skill}」访问数据源「{source}」吗?",
32125
+ "interaction.sandbox.writeArtifact": "允许技能「{skill}」写出产物「{path}」吗?",
32126
+ "interaction.sandbox.confirm": "技能「{skill}」请求确认:",
32067
32127
  "interaction.traceEvidence.title": "该技能声称的步骤",
32068
32128
  "interaction.traceEvidence.columnDraft": "技能里写的",
32069
32129
  "interaction.traceEvidence.columnTrace": "本会话实际执行的",
@@ -32137,6 +32197,20 @@ const chatbotDictionary = defineDictionary({
32137
32197
  "composer.dictate.recording": "录音中",
32138
32198
  "composer.dictate.api-missing": "当前浏览器不支持语音识别。",
32139
32199
  "composer.dictate.insecure-context": "语音输入需要安全连接(https 或 localhost)。",
32200
+ "composer.dictate.denied": "浏览器拒绝了麦克风。请在站点权限设置里允许麦克风后重试。",
32201
+ "composer.dictate.denied.handoff": "麦克风需要在普通标签页里授权一次。请在那里完成授权,再回到这里重试。",
32202
+ "composer.dictate.failed": "语音输入意外中断。可以再试一次,或直接打字。",
32203
+ "composer.camera": "拍照",
32204
+ "composer.camera.title": "拍照",
32205
+ "composer.camera.description": "拍好的照片会作为附件放进输入框,你点发送才会发出去。",
32206
+ "composer.camera.preview": "摄像头取景",
32207
+ "composer.camera.shutter": "拍照",
32208
+ "composer.camera.noStorage": "当前宿主不支持附件",
32209
+ "composer.camera.api-missing": "当前浏览器不支持调用摄像头。",
32210
+ "composer.camera.insecure-context": "使用摄像头需要安全连接(https 或 localhost)。",
32211
+ "composer.camera.denied": "浏览器拒绝了摄像头。请在站点权限设置里允许摄像头后重试。",
32212
+ "composer.camera.denied.handoff": "摄像头需要在普通标签页里授权一次。请在那里完成授权,再回到这里重试。",
32213
+ "composer.camera.failed": "没能拍下这张照片。可以再试一次,或改为直接添加图片文件。",
32140
32214
  "settings.title": "设置",
32141
32215
  "settings.group.general": "通用",
32142
32216
  "settings.group.models": "模型",
@@ -34273,6 +34347,7 @@ function assembleAssistantParts(source) {
34273
34347
  ...source.skills.map(skillPart),
34274
34348
  ...source.perceptions.map(perceptionPart),
34275
34349
  ...source.runDetails.map((item) => item.part),
34350
+ ...source.precedingBody ?? [],
34276
34351
  ...bodyParts(source.contentParts, source.text),
34277
34352
  ...source.interactions.map(interactionPart),
34278
34353
  ...source.candidates.map(skillCandidatePart),
@@ -34295,15 +34370,24 @@ function terminationParts(phases) {
34295
34370
  }
34296
34371
  }];
34297
34372
  }
34373
+ /** 引用在「已落盘」集合里的键;blocks 一个 run 至多一个,无需再分 */
34374
+ const refKey = (part) => part.type === "surface" ? `surface:${part.surfaceId}` : "blocks";
34298
34375
  /**
34299
34376
  * 同 run 的运行细节合并计划(UI-UX5 #7):FR-17.8 的交互切片会让一个 run 产生多条
34300
34377
  * assistant 消息,每条各自组装「运行细节」就是用户看到的两个框(run id 相同)。
34301
34378
  * 这里把相位/技能/工具并入**第一条片段**(表单上方),后续片段与续跑 live 不再出框。
34302
34379
  * 相位本来就是无重叠的增量切片(`#flushed` 水位),直接拼接即全量;
34303
34380
  * live 的相位是全量累积,增量 = 全量减去已落盘数。
34381
+ *
34382
+ * 分册 16:run 未终止时还要把已切片载体本身并进 live 气泡(`hidden`)。
34383
+ * 切片会把写到一半的正文从 live 气泡搬到一个新 id 的消息上,
34384
+ * live 气泡随即被清空再由下一轮填满——每次工具调用都卸载/重建一次 DOM,
34385
+ * 就是用户看到的「正文下半段删掉又补新的进去、高度一会变小一会变大」。
34386
+ * 合并后一个进行中的 run 始终只有一条气泡,内容只增不减;终态消息到达后才恢复切片形态(历史不变)。
34304
34387
  */
34305
34388
  function planRunTrace(messages, live) {
34306
34389
  const byMessage = /* @__PURE__ */ new Map();
34390
+ const hidden = /* @__PURE__ */ new Set();
34307
34391
  let livePlan;
34308
34392
  const byRun = /* @__PURE__ */ new Map();
34309
34393
  for (const message of messages) {
@@ -34344,16 +34428,27 @@ function planRunTrace(messages, live) {
34344
34428
  terminationPhases: message.phases ?? []
34345
34429
  });
34346
34430
  });
34347
- if (liveHere) livePlan = {
34348
- phases: [],
34349
- skills: [],
34350
- runDetails: [],
34351
- settled: false,
34352
- terminationPhases: liveHere.phases
34353
- };
34431
+ if (liveHere) {
34432
+ const refs = /* @__PURE__ */ new Set();
34433
+ for (const message of carriers) for (const part of message.contentParts ?? []) if (part.type !== "text") refs.add(refKey(part));
34434
+ for (const message of carriers) hidden.add(message.id);
34435
+ livePlan = {
34436
+ phases: mergedPhases,
34437
+ skills: mergedSkills,
34438
+ runDetails: mergedRunDetails,
34439
+ settled: false,
34440
+ terminationPhases: liveHere.phases,
34441
+ merged: {
34442
+ carriers,
34443
+ text: carriers.reduce((total, m) => total + (m.text?.length ?? 0), 0),
34444
+ refs
34445
+ }
34446
+ };
34447
+ }
34354
34448
  }
34355
34449
  return {
34356
34450
  byMessage,
34451
+ hidden,
34357
34452
  ...livePlan ? { live: livePlan } : {}
34358
34453
  };
34359
34454
  }
@@ -34406,6 +34501,9 @@ function liveTiming(live) {
34406
34501
  };
34407
34502
  }
34408
34503
  function liveMessage(live, trace) {
34504
+ const from = trace?.merged?.text ?? 0;
34505
+ const flushedRefs = trace?.merged?.refs;
34506
+ const precedingBody = (trace?.merged?.carriers ?? []).flatMap((message) => [...bodyParts(message.contentParts, message.text), ...(message.interactions ?? []).map(interactionPart)]);
34409
34507
  const content = assembleAssistantParts({
34410
34508
  phases: trace?.phases ?? live.phases,
34411
34509
  skills: trace?.skills ?? live.skills,
@@ -34414,8 +34512,9 @@ function liveMessage(live, trace) {
34414
34512
  part: liveToolPart(call),
34415
34513
  at: call.startedAt
34416
34514
  })), live.thinkings),
34417
- text: live.text,
34418
- contentParts: materialize(live.ledger, live.text, 0, { includePending: true }),
34515
+ precedingBody,
34516
+ text: live.text.slice(from),
34517
+ contentParts: materialize(live.ledger, live.text, from, { includePending: true }).filter((part) => part.type === "text" || flushedRefs === void 0 || !flushedRefs.has(refKey(part))),
34419
34518
  interactions: [],
34420
34519
  candidates: [],
34421
34520
  settled: trace?.settled ?? false,
@@ -34450,13 +34549,14 @@ function liveMessage(live, trace) {
34450
34549
  */
34451
34550
  function toExternalMessages(messages, live, cache) {
34452
34551
  const trace = planRunTrace(messages, live);
34453
- const result = messages.map((message) => {
34454
- if (message.role === "user") return userMessage(message);
34552
+ const result = messages.flatMap((message) => {
34553
+ if (message.role === "user") return [userMessage(message)];
34554
+ if (trace.hidden.has(message.id)) return [];
34455
34555
  const override = trace.byMessage.get(message.id);
34456
34556
  const cached = cache?.get(message.id);
34457
34557
  const external = cached !== void 0 && override === void 0 ? cached : assistantMessage(message, override);
34458
34558
  if (override === void 0) cache?.set(message.id, external);
34459
- return external;
34559
+ return [external];
34460
34560
  });
34461
34561
  const settled = messages.some((message) => message.role === "assistant" && message.runId === live?.runId && message.interactions === void 0);
34462
34562
  if (live && !settled) result.push(liveMessage(live, trace.live));
@@ -34978,6 +35078,124 @@ function InteractionWaitBar({ bridge, label, elsewhere = false, panel, onLocate
34978
35078
  });
34979
35079
  }
34980
35080
 
35081
+ //#endregion
35082
+ //#region src/react/CameraCaptureDialog.tsx
35083
+ /**
35084
+ * 取景与快门(分册 14 FR-14.5)。
35085
+ *
35086
+ * 点遮罩不关闭:摄像头开着时误点一下就得重新授权取流,代价与「丢掉表单草稿」同级。
35087
+ * 预览框按 4:3 留位,避免流还没出画时对话框高度跳一下。
35088
+ */
35089
+ function CameraCaptureDialog({ open, busy, videoRef, onCapture, onCancel }) {
35090
+ const t = useT();
35091
+ return /* @__PURE__ */ jsx(Dialog, {
35092
+ open,
35093
+ onClose: onCancel,
35094
+ dismissOnOutsideClick: false,
35095
+ title: t("composer.camera.title"),
35096
+ description: t("composer.camera.description"),
35097
+ size: "lg",
35098
+ footer: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Button, {
35099
+ variant: "secondary",
35100
+ onClick: onCancel,
35101
+ children: t("common.cancel")
35102
+ }), /* @__PURE__ */ jsx(Button, {
35103
+ "data-testid": "composer-camera-shutter",
35104
+ icon: /* @__PURE__ */ jsx(Camera, { className: "size-4" }),
35105
+ disabled: busy,
35106
+ onClick: onCapture,
35107
+ children: t("composer.camera.shutter")
35108
+ })] }),
35109
+ children: /* @__PURE__ */ jsx("video", {
35110
+ ref: videoRef,
35111
+ "data-testid": "composer-camera-preview",
35112
+ "aria-label": t("composer.camera.preview"),
35113
+ autoPlay: true,
35114
+ muted: true,
35115
+ playsInline: true,
35116
+ className: "aspect-[4/3] w-full rounded-card bg-subtle object-contain"
35117
+ })
35118
+ });
35119
+ }
35120
+
35121
+ //#endregion
35122
+ //#region src/react/useComposerCamera.ts
35123
+ /**
35124
+ * composer 的拍照接线(分册 14)。照片只作为附件落进输入框,**不发送**——
35125
+ * 与语音同一条口径:拍歪了要能先删掉再重拍,自动发送会把误拍变成不可撤销的操作。
35126
+ */
35127
+ function useComposerCamera(onPermissionDenied) {
35128
+ const aui = useAui();
35129
+ const [availability] = useState(() => checkCameraAvailability());
35130
+ const [previewing, setPreviewing] = useState(false);
35131
+ const [busy, setBusy] = useState(false);
35132
+ const [failure, setFailure] = useState();
35133
+ const sessionRef = useRef(void 0);
35134
+ const videoElementRef = useRef(null);
35135
+ const close = useCallback(() => {
35136
+ sessionRef.current?.close();
35137
+ sessionRef.current = void 0;
35138
+ if (videoElementRef.current !== null) videoElementRef.current.srcObject = null;
35139
+ setPreviewing(false);
35140
+ setBusy(false);
35141
+ }, []);
35142
+ useEffect(() => () => sessionRef.current?.close(), []);
35143
+ /** 侧栏 / popup 这类页面弹不出授权框,只有宿主知道该把用户带到哪儿去(FR-14.7) */
35144
+ const report = useCallback((e) => {
35145
+ const denied = e instanceof WebSkillError && e.code === "CAMERA_PERMISSION_DENIED";
35146
+ setFailure(denied ? "denied" : "failed");
35147
+ if (denied) onPermissionDenied?.();
35148
+ }, [onPermissionDenied]);
35149
+ const videoRef = useCallback((element) => {
35150
+ videoElementRef.current = element;
35151
+ if (element === null) return;
35152
+ const stream = sessionRef.current?.stream;
35153
+ if (stream === void 0) return;
35154
+ element.srcObject = stream;
35155
+ element.play().catch(() => void 0);
35156
+ }, []);
35157
+ return {
35158
+ availability,
35159
+ previewing,
35160
+ busy,
35161
+ start: useCallback(() => {
35162
+ if (!availability.available || busy || previewing) return;
35163
+ setFailure(void 0);
35164
+ setBusy(true);
35165
+ openCamera().then((session) => {
35166
+ sessionRef.current = session;
35167
+ setPreviewing(true);
35168
+ setBusy(false);
35169
+ }).catch((e) => {
35170
+ setBusy(false);
35171
+ report(e);
35172
+ });
35173
+ }, [
35174
+ availability,
35175
+ busy,
35176
+ previewing,
35177
+ report
35178
+ ]),
35179
+ cancel: close,
35180
+ capture: useCallback(() => {
35181
+ const video = videoElementRef.current;
35182
+ if (video === null || busy) return;
35183
+ setBusy(true);
35184
+ capturePhoto(video).then((file) => aui.thread.composer().addAttachment(file)).then(() => close()).catch((e) => {
35185
+ close();
35186
+ report(e);
35187
+ });
35188
+ }, [
35189
+ aui,
35190
+ busy,
35191
+ close,
35192
+ report
35193
+ ]),
35194
+ videoRef,
35195
+ ...failure !== void 0 ? { failure } : {}
35196
+ };
35197
+ }
35198
+
34981
35199
  //#endregion
34982
35200
  //#region src/react/useComposerDictation.ts
34983
35201
  /** 转写片段拼到已有草稿后面:语音只做追加,不覆盖用户已经打好的字 */
@@ -34990,11 +35208,11 @@ function joinText(base, addition) {
34990
35208
  * composer 的语音输入接线(需求 7)。转写结果只写进输入框,**不发送**(FR-7.8)——
34991
35209
  * 语音识别不准是常态,自动发送会把识别错误变成不可撤销的操作。
34992
35210
  */
34993
- function useComposerDictation(lang) {
35211
+ function useComposerDictation(lang, onPermissionDenied) {
34994
35212
  const aui = useAui();
34995
35213
  const [availability] = useState(() => checkDictationAvailability());
34996
35214
  const [recording, setRecording] = useState(false);
34997
- const [error, setError] = useState();
35215
+ const [failure, setFailure] = useState();
34998
35216
  const sessionRef = useRef(void 0);
34999
35217
  /** 本次录音开始时输入框里的内容;中间结果每次都在它后面重画,否则会自我叠加 */
35000
35218
  const baseRef = useRef("");
@@ -35004,6 +35222,12 @@ function useComposerDictation(lang) {
35004
35222
  setRecording(false);
35005
35223
  }, []);
35006
35224
  useEffect(() => () => sessionRef.current?.stop(), []);
35225
+ /** 侧栏 / popup 这类页面弹不出授权框,只有宿主知道该把用户带到哪儿去(FR-10.1) */
35226
+ const report = useCallback((e) => {
35227
+ const denied = e instanceof WebSkillError && e.code === "DICTATION_PERMISSION_DENIED";
35228
+ setFailure(denied ? "denied" : "failed");
35229
+ if (denied) onPermissionDenied?.();
35230
+ }, [onPermissionDenied]);
35007
35231
  return {
35008
35232
  availability,
35009
35233
  recording,
@@ -35015,7 +35239,7 @@ function useComposerDictation(lang) {
35015
35239
  if (!availability.available) return;
35016
35240
  const composer = aui.thread.composer();
35017
35241
  baseRef.current = composer.getState().text;
35018
- setError(void 0);
35242
+ setFailure(void 0);
35019
35243
  try {
35020
35244
  sessionRef.current = startDictation({
35021
35245
  lang,
@@ -35025,21 +35249,22 @@ function useComposerDictation(lang) {
35025
35249
  aui.thread.composer().setText(baseRef.current);
35026
35250
  },
35027
35251
  onError: (e) => {
35028
- setError(messageOf(e));
35252
+ report(e);
35029
35253
  stop();
35030
35254
  }
35031
35255
  });
35032
35256
  setRecording(true);
35033
35257
  } catch (e) {
35034
- setError(messageOf(e));
35258
+ report(e);
35035
35259
  }
35036
35260
  }, [
35037
35261
  aui,
35038
35262
  availability,
35039
35263
  lang,
35264
+ report,
35040
35265
  stop
35041
35266
  ]),
35042
- ...error !== void 0 ? { error } : {}
35267
+ ...failure !== void 0 ? { failure } : {}
35043
35268
  };
35044
35269
  }
35045
35270
 
@@ -35114,13 +35339,22 @@ function ModelMenuButton({ models }) {
35114
35339
  }, option.id))
35115
35340
  })] });
35116
35341
  }
35117
- function AssistantUiComposer({ running, disabled = false, waitTarget, placeholder, attachments = false, attachmentInfo, attachmentImages, centered = false, models, noModel, dictationLang, capabilities }) {
35342
+ function AssistantUiComposer({ running, disabled = false, waitTarget, placeholder, attachments = false, attachmentInfo, attachmentImages, centered = false, models, noModel, dictationLang, onDictationPermissionDenied, onCameraPermissionDenied, capabilities }) {
35118
35343
  const t = useT();
35119
35344
  const locale = useLocale();
35120
- const dictation = useComposerDictation(dictationLang ?? DEFAULT_DICTATION_LANG[locale]);
35345
+ const dictation = useComposerDictation(dictationLang ?? DEFAULT_DICTATION_LANG[locale], onDictationPermissionDenied);
35346
+ const camera = useComposerCamera(onCameraPermissionDenied);
35121
35347
  const dictationReason = dictation.availability.available ? void 0 : t(`composer.dictate.${dictation.availability.reason}`);
35348
+ const dictationFailure = dictation.failure === void 0 ? void 0 : dictation.failure === "denied" && onDictationPermissionDenied !== void 0 ? t("composer.dictate.denied.handoff") : t(`composer.dictate.${dictation.failure}`);
35122
35349
  const waitReason = disabled ? t("interaction.wait", { target: waitTarget ?? t("interaction.wait.fallback") }) : void 0;
35123
35350
  const attachmentReason = attachmentImages === void 0 || attachmentImages.enabled && attachmentImages.imageCapable ? void 0 : attachmentImages.enabled ? t("composer.addAttachment.modelNoImages") : t("composer.addAttachment.imagesOff");
35351
+ /**
35352
+ * 相机的禁用原因(FR-14.2)。三道门缺一不可,且顺序即优先级:
35353
+ * 存不下 → 图片被关 / 模型不收图 → 浏览器不支持。先说最外层那道,
35354
+ * 否则用户会照着「换 https」折腾半天,其实是模型压根不收图。
35355
+ */
35356
+ const cameraReason = !attachments ? t("composer.camera.noStorage") : attachmentReason ?? (camera.availability.available ? void 0 : t(`composer.camera.${camera.availability.reason}`));
35357
+ const cameraFailure = camera.failure === void 0 ? void 0 : camera.failure === "denied" && onCameraPermissionDenied !== void 0 ? t("composer.camera.denied.handoff") : t(`composer.camera.${camera.failure}`);
35124
35358
  const plainChatHintId = useId();
35125
35359
  return /* @__PURE__ */ jsx(ComposerPrimitive.Root, {
35126
35360
  "data-testid": "assistant-ui-composer",
@@ -35254,70 +35488,102 @@ function AssistantUiComposer({ running, disabled = false, waitTarget, placeholde
35254
35488
  ]
35255
35489
  }), /* @__PURE__ */ jsxs("div", {
35256
35490
  className: "flex shrink-0 items-center gap-1",
35257
- children: [/* @__PURE__ */ jsxs("button", {
35258
- type: "button",
35259
- "data-testid": "composer-dictate",
35260
- "data-recording": dictation.recording ? "true" : void 0,
35261
- "aria-pressed": dictation.recording,
35262
- "aria-label": dictation.recording ? t("composer.dictate.stop") : t("composer.dictate"),
35263
- title: dictationReason ?? (dictation.recording ? t("composer.dictate.stop") : t("composer.dictate")),
35264
- disabled: dictationReason !== void 0,
35265
- onClick: dictation.toggle,
35266
- className: `inline-flex size-8 shrink-0 items-center justify-center rounded-full transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${dictation.recording ? "animate-pulse bg-danger/10 text-danger" : "text-muted hover:bg-accent hover:text-ink"}`,
35267
- children: [dictation.recording ? /* @__PURE__ */ jsx(Square, {
35268
- className: "size-3 fill-current",
35269
- "aria-hidden": true
35270
- }) : /* @__PURE__ */ jsx(Mic, {
35271
- className: "size-4",
35272
- "aria-hidden": true
35273
- }), /* @__PURE__ */ jsx("span", {
35274
- className: "sr-only",
35275
- children: dictation.recording ? t("composer.dictate.recording") : t("composer.dictate")
35276
- })]
35277
- }), running ? /* @__PURE__ */ jsxs(ComposerPrimitive.Cancel, {
35278
- "data-testid": "composer-send",
35279
- "aria-label": t("composer.stop"),
35280
- className: "inline-flex size-8 shrink-0 items-center justify-center rounded-full border border-border text-ink transition-colors hover:bg-accent",
35281
- children: [/* @__PURE__ */ jsx(Square, {
35282
- className: "size-3 fill-current",
35283
- "aria-hidden": true
35284
- }), /* @__PURE__ */ jsx("span", {
35285
- className: "sr-only",
35286
- children: t("composer.stop")
35287
- })]
35288
- }) : noModel ? /* @__PURE__ */ jsxs("button", {
35289
- type: "button",
35290
- "data-testid": "composer-send",
35291
- "aria-label": t("composer.send"),
35292
- onClick: noModel.onConfigure,
35293
- className: "webskill-aui-send inline-flex size-8 shrink-0 items-center justify-center rounded-full transition-colors disabled:cursor-not-allowed disabled:opacity-50",
35294
- children: [/* @__PURE__ */ jsx(ArrowUp, {
35295
- className: "size-4",
35296
- "aria-hidden": true
35297
- }), /* @__PURE__ */ jsx("span", {
35298
- className: "sr-only",
35299
- children: t("composer.send")
35300
- })]
35301
- }) : /* @__PURE__ */ jsxs(ComposerPrimitive.Send, {
35302
- "data-testid": "composer-send",
35303
- "aria-label": t("composer.send"),
35304
- className: "webskill-aui-send inline-flex size-8 shrink-0 items-center justify-center rounded-full transition-colors disabled:cursor-not-allowed disabled:opacity-50",
35305
- children: [/* @__PURE__ */ jsx(ArrowUp, {
35306
- className: "size-4",
35307
- "aria-hidden": true
35308
- }), /* @__PURE__ */ jsx("span", {
35309
- className: "sr-only",
35310
- children: t("composer.send")
35311
- })]
35312
- })]
35491
+ children: [
35492
+ /* @__PURE__ */ jsxs("button", {
35493
+ type: "button",
35494
+ "data-testid": "composer-camera",
35495
+ "aria-label": t("composer.camera"),
35496
+ title: cameraReason === void 0 ? t("composer.camera") : `${t("composer.camera")} — ${cameraReason}`,
35497
+ disabled: cameraReason !== void 0 || camera.busy,
35498
+ onClick: camera.start,
35499
+ className: "inline-flex size-8 shrink-0 items-center justify-center rounded-full text-muted transition-colors hover:bg-accent hover:text-ink disabled:cursor-not-allowed disabled:opacity-50",
35500
+ children: [/* @__PURE__ */ jsx(Camera, {
35501
+ className: "size-4",
35502
+ "aria-hidden": true
35503
+ }), /* @__PURE__ */ jsx("span", {
35504
+ className: "sr-only",
35505
+ children: t("composer.camera")
35506
+ })]
35507
+ }),
35508
+ /* @__PURE__ */ jsxs("button", {
35509
+ type: "button",
35510
+ "data-testid": "composer-dictate",
35511
+ "data-recording": dictation.recording ? "true" : void 0,
35512
+ "aria-pressed": dictation.recording,
35513
+ "aria-label": dictation.recording ? t("composer.dictate.stop") : t("composer.dictate"),
35514
+ title: dictationReason ?? (dictation.recording ? t("composer.dictate.stop") : t("composer.dictate")),
35515
+ disabled: dictationReason !== void 0,
35516
+ onClick: dictation.toggle,
35517
+ className: `inline-flex size-8 shrink-0 items-center justify-center rounded-full transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${dictation.recording ? "animate-pulse bg-danger/10 text-danger" : "text-muted hover:bg-accent hover:text-ink"}`,
35518
+ children: [dictation.recording ? /* @__PURE__ */ jsx(Square, {
35519
+ className: "size-3 fill-current",
35520
+ "aria-hidden": true
35521
+ }) : /* @__PURE__ */ jsx(Mic, {
35522
+ className: "size-4",
35523
+ "aria-hidden": true
35524
+ }), /* @__PURE__ */ jsx("span", {
35525
+ className: "sr-only",
35526
+ children: dictation.recording ? t("composer.dictate.recording") : t("composer.dictate")
35527
+ })]
35528
+ }),
35529
+ running ? /* @__PURE__ */ jsxs(ComposerPrimitive.Cancel, {
35530
+ "data-testid": "composer-send",
35531
+ "aria-label": t("composer.stop"),
35532
+ className: "inline-flex size-8 shrink-0 items-center justify-center rounded-full border border-border text-ink transition-colors hover:bg-accent",
35533
+ children: [/* @__PURE__ */ jsx(Square, {
35534
+ className: "size-3 fill-current",
35535
+ "aria-hidden": true
35536
+ }), /* @__PURE__ */ jsx("span", {
35537
+ className: "sr-only",
35538
+ children: t("composer.stop")
35539
+ })]
35540
+ }) : noModel ? /* @__PURE__ */ jsxs("button", {
35541
+ type: "button",
35542
+ "data-testid": "composer-send",
35543
+ "aria-label": t("composer.send"),
35544
+ onClick: noModel.onConfigure,
35545
+ className: "webskill-aui-send inline-flex size-8 shrink-0 items-center justify-center rounded-full transition-colors disabled:cursor-not-allowed disabled:opacity-50",
35546
+ children: [/* @__PURE__ */ jsx(ArrowUp, {
35547
+ className: "size-4",
35548
+ "aria-hidden": true
35549
+ }), /* @__PURE__ */ jsx("span", {
35550
+ className: "sr-only",
35551
+ children: t("composer.send")
35552
+ })]
35553
+ }) : /* @__PURE__ */ jsxs(ComposerPrimitive.Send, {
35554
+ "data-testid": "composer-send",
35555
+ "aria-label": t("composer.send"),
35556
+ className: "webskill-aui-send inline-flex size-8 shrink-0 items-center justify-center rounded-full transition-colors disabled:cursor-not-allowed disabled:opacity-50",
35557
+ children: [/* @__PURE__ */ jsx(ArrowUp, {
35558
+ className: "size-4",
35559
+ "aria-hidden": true
35560
+ }), /* @__PURE__ */ jsx("span", {
35561
+ className: "sr-only",
35562
+ children: t("composer.send")
35563
+ })]
35564
+ })
35565
+ ]
35313
35566
  })]
35314
35567
  }),
35315
- dictation.error !== void 0 ? /* @__PURE__ */ jsx("p", {
35568
+ dictationFailure !== void 0 ? /* @__PURE__ */ jsx("p", {
35316
35569
  "data-testid": "composer-dictate-error",
35317
35570
  role: "alert",
35318
35571
  className: "text-xs text-danger",
35319
- children: dictation.error
35320
- }) : null
35572
+ children: dictationFailure
35573
+ }) : null,
35574
+ cameraFailure !== void 0 ? /* @__PURE__ */ jsx("p", {
35575
+ "data-testid": "composer-camera-error",
35576
+ role: "alert",
35577
+ className: "text-xs text-danger",
35578
+ children: cameraFailure
35579
+ }) : null,
35580
+ /* @__PURE__ */ jsx(CameraCaptureDialog, {
35581
+ open: camera.previewing,
35582
+ busy: camera.busy,
35583
+ videoRef: camera.videoRef,
35584
+ onCapture: camera.capture,
35585
+ onCancel: camera.cancel
35586
+ })
35321
35587
  ]
35322
35588
  })
35323
35589
  });
@@ -45881,6 +46147,159 @@ function InlineBlocks() {
45881
46147
  });
45882
46148
  }
45883
46149
 
46150
+ //#endregion
46151
+ //#region src/react/authorizeDetails.ts
46152
+ /** 页面操作确认卡的载荷;形状不对就整段不渲染,退回通用授权展示 */
46153
+ function pageActionDetails(details) {
46154
+ if (typeof details !== "object" || details === null) return void 0;
46155
+ const record = details;
46156
+ const action = record["action"];
46157
+ const target = record["target"];
46158
+ if (typeof action !== "string" || !PAGE_ACTION_KINDS.includes(action)) return void 0;
46159
+ if (typeof target !== "object" || target === null) return void 0;
46160
+ const { role, name, frame } = target;
46161
+ if (typeof role !== "string") return void 0;
46162
+ const value = record["value"];
46163
+ const direction = record["direction"];
46164
+ const rememberLabel = record["rememberLabel"];
46165
+ return {
46166
+ action,
46167
+ role,
46168
+ ...typeof name === "string" ? { name } : {},
46169
+ ...typeof value === "string" ? { value } : {},
46170
+ ...direction === "down" || direction === "up" ? { direction } : {},
46171
+ ...typeof frame === "string" ? { frame } : {},
46172
+ ...record["elevated"] === true ? { elevated: true } : {},
46173
+ ...record["rememberable"] === true ? { rememberable: true } : {},
46174
+ ...typeof rememberLabel === "string" ? { rememberLabel } : {}
46175
+ };
46176
+ }
46177
+ /**
46178
+ * 页面操作确认卡的正文(0.18.0 设计总览 §3)。
46179
+ *
46180
+ * 活卡(`InteractionCard`)与只读回看走**同一个函数**。0.17.0 分册 19 已经写明
46181
+ * 「这份映射只能有一份」,当时只统一了入口没有合并实现;`scroll` 是第一个要
46182
+ * **按参数分岔**的动作(向下/向上),两份实现会立刻分叉成「活卡说向下、回看说滚动」。
46183
+ */
46184
+ function pageActionMessage(details, t) {
46185
+ const target = details.name ?? t(`interaction.pageAction.role.${details.action}`);
46186
+ if (details.action === "scroll") return t(`interaction.pageAction.scroll.${details.direction ?? "down"}`, { target });
46187
+ return t(`interaction.pageAction.${details.action}`, { target });
46188
+ }
46189
+ /**
46190
+ * 下载文件确认卡的载荷(0.14.0 分册 20)。与 `pageActionDetails` 同一条规矩:
46191
+ * 形状不对就整段不渲染,退回通用授权展示,不猜。
46192
+ */
46193
+ function downloadedFileDetails(details) {
46194
+ if (typeof details !== "object" || details === null) return void 0;
46195
+ const record = details;
46196
+ const action = record["action"];
46197
+ if (action !== "list" && action !== "read") return void 0;
46198
+ const file = record["file"];
46199
+ const rememberLabel = record["rememberLabel"];
46200
+ const named = typeof file === "object" && file !== null ? file : void 0;
46201
+ return {
46202
+ action,
46203
+ ...typeof named?.["name"] === "string" && typeof named["size"] === "number" ? { file: {
46204
+ name: named["name"],
46205
+ size: named["size"]
46206
+ } } : {},
46207
+ ...record["rememberable"] === true ? { rememberable: true } : {},
46208
+ ...typeof rememberLabel === "string" ? { rememberLabel } : {}
46209
+ };
46210
+ }
46211
+ /** 上传文件确认卡的载荷(0.15.0 分册 17,0.17.0 分册 19 起结构化) */
46212
+ function uploadFileDetails(details) {
46213
+ if (typeof details !== "object" || details === null) return void 0;
46214
+ const record = details;
46215
+ const skill = record["skill"];
46216
+ if (typeof skill !== "string") return void 0;
46217
+ const file = record["file"];
46218
+ const named = typeof file === "object" && file !== null ? file : void 0;
46219
+ return {
46220
+ skill,
46221
+ ...typeof named?.["name"] === "string" && typeof named["contentType"] === "string" && typeof named["size"] === "number" ? { file: {
46222
+ name: named["name"],
46223
+ contentType: named["contentType"],
46224
+ size: named["size"]
46225
+ } } : {}
46226
+ };
46227
+ }
46228
+ /** 五个沙箱能力闸门的共用载荷(`gate()` 统一注入 `skill`,其余按能力各带一项) */
46229
+ function sandboxDetails(details) {
46230
+ if (typeof details !== "object" || details === null) return void 0;
46231
+ const record = details;
46232
+ const skill = record["skill"];
46233
+ if (typeof skill !== "string") return void 0;
46234
+ const out = { skill };
46235
+ for (const key of [
46236
+ "path",
46237
+ "sourceId",
46238
+ "question"
46239
+ ]) {
46240
+ const value = record[key];
46241
+ if (typeof value === "string") out[key] = value;
46242
+ }
46243
+ return out;
46244
+ }
46245
+ /**
46246
+ * 授权卡正文的本地化文案(分册 19 FR-19.2 / FR-19.6)。
46247
+ *
46248
+ * 活卡、只读回看、非 native 三档的 spec 通道读的是同一个 `request`,因此这份映射
46249
+ * 只能有一份:写进任一视图都会让另外两个继续显示 runtime 的英文原句——
46250
+ * 0.12.0 分册 23 与 0.16.0 分册 15 各犯过一次。
46251
+ * 认不得的能力或形状不对的载荷返回 undefined,正文回落到 `request.message`(FR-19.5)。
46252
+ */
46253
+ function localizedAuthorizeMessage(request, t) {
46254
+ if (request.type !== "authorize") return void 0;
46255
+ if (request.capability === "readUploadFile") {
46256
+ const details = uploadFileDetails(request.details);
46257
+ if (details === void 0) return void 0;
46258
+ const head = t("interaction.uploadFile.read", { skill: details.skill });
46259
+ if (details.file === void 0) return head;
46260
+ return `${head} ${t("interaction.uploadFile.file", {
46261
+ name: details.file.name,
46262
+ type: details.file.contentType,
46263
+ size: formatBytes(details.file.size)
46264
+ })}`;
46265
+ }
46266
+ if (request.capability === "readReference" || request.capability === "readAsset" || request.capability === "writeArtifact" || request.capability === "fetchData" || request.capability === "confirm") {
46267
+ const details = sandboxDetails(request.details);
46268
+ if (details === void 0) return void 0;
46269
+ if (request.capability === "fetchData") {
46270
+ if (details["sourceId"] === void 0) return void 0;
46271
+ return t("interaction.sandbox.fetchData", {
46272
+ skill: details["skill"],
46273
+ source: details["sourceId"]
46274
+ });
46275
+ }
46276
+ if (request.capability === "confirm") {
46277
+ const head = t("interaction.sandbox.confirm", { skill: details["skill"] });
46278
+ return details["question"] === void 0 ? head : `${head} ${details["question"]}`;
46279
+ }
46280
+ if (details["path"] === void 0) return void 0;
46281
+ return t(`interaction.sandbox.${request.capability}`, {
46282
+ skill: details["skill"],
46283
+ path: details["path"]
46284
+ });
46285
+ }
46286
+ if (request.capability === "pageAction") {
46287
+ const details = pageActionDetails(request.details);
46288
+ if (details === void 0) return void 0;
46289
+ return pageActionMessage(details, t);
46290
+ }
46291
+ if (request.capability === "readDownloadedFile") {
46292
+ const details = downloadedFileDetails(request.details);
46293
+ if (details === void 0) return void 0;
46294
+ const head = t(`interaction.downloadedFile.${details.action}`);
46295
+ if (details.file === void 0) return head;
46296
+ return `${head} ${t("interaction.downloadedFile.file", {
46297
+ name: details.file.name,
46298
+ size: formatBytes(details.file.size)
46299
+ })}`;
46300
+ }
46301
+ }
46302
+
45884
46303
  //#endregion
45885
46304
  //#region src/react/interactionLabels.ts
45886
46305
  /**
@@ -45900,10 +46319,12 @@ function interactionTitleKey(request) {
45900
46319
  * 四档共用本函数,切换渲染档不会让同一个请求出现两套文案。
45901
46320
  */
45902
46321
  function interactionLabels(request, t) {
46322
+ const message = localizedAuthorizeMessage(request, t);
45903
46323
  return {
45904
46324
  title: request.type === "form" && request.title ? request.title : t(interactionTitleKey(request)),
45905
46325
  submitLabel: t(request.type === "authorize" ? "interaction.allow" : request.type === "confirm" ? "interaction.confirm" : "interaction.submit"),
45906
- cancelLabel: t(request.type === "authorize" ? "interaction.deny" : "interaction.cancel")
46326
+ cancelLabel: t(request.type === "authorize" ? "interaction.deny" : "interaction.cancel"),
46327
+ ...message !== void 0 ? { message } : {}
45907
46328
  };
45908
46329
  }
45909
46330
 
@@ -47441,51 +47862,6 @@ function fileToBase64(file) {
47441
47862
  reader.readAsDataURL(file);
47442
47863
  });
47443
47864
  }
47444
- /** 页面操作确认卡的载荷;形状不对就整段不渲染,退回通用授权展示 */
47445
- function pageActionDetails(details) {
47446
- if (typeof details !== "object" || details === null) return void 0;
47447
- const record = details;
47448
- const action = record["action"];
47449
- const target = record["target"];
47450
- if (typeof action !== "string" || !PAGE_ACTION_KINDS.includes(action)) return void 0;
47451
- if (typeof target !== "object" || target === null) return void 0;
47452
- const { role, name, frame } = target;
47453
- if (typeof role !== "string") return void 0;
47454
- const value = record["value"];
47455
- const rememberLabel = record["rememberLabel"];
47456
- return {
47457
- action,
47458
- role,
47459
- ...typeof name === "string" ? { name } : {},
47460
- ...typeof value === "string" ? { value } : {},
47461
- ...typeof frame === "string" ? { frame } : {},
47462
- ...record["elevated"] === true ? { elevated: true } : {},
47463
- ...record["rememberable"] === true ? { rememberable: true } : {},
47464
- ...typeof rememberLabel === "string" ? { rememberLabel } : {}
47465
- };
47466
- }
47467
- /**
47468
- * 下载文件确认卡的载荷(0.14.0 分册 20)。与 `pageActionDetails` 同一条规矩:
47469
- * 形状不对就整段不渲染,退回通用授权展示,不猜。
47470
- */
47471
- function downloadedFileDetails(details) {
47472
- if (typeof details !== "object" || details === null) return void 0;
47473
- const record = details;
47474
- const action = record["action"];
47475
- if (action !== "list" && action !== "read") return void 0;
47476
- const file = record["file"];
47477
- const rememberLabel = record["rememberLabel"];
47478
- const named = typeof file === "object" && file !== null ? file : void 0;
47479
- return {
47480
- action,
47481
- ...typeof named?.["name"] === "string" && typeof named["size"] === "number" ? { file: {
47482
- name: named["name"],
47483
- size: named["size"]
47484
- } } : {},
47485
- ...record["rememberable"] === true ? { rememberable: true } : {},
47486
- ...typeof rememberLabel === "string" ? { rememberLabel } : {}
47487
- };
47488
- }
47489
47865
  /**
47490
47866
  * 轨迹证据载荷(分册 30)。形状不对就整段不渲染,退回 `preview` 纯文本——
47491
47867
  * 与 `pageActionDetails` 同一个先例:认不得的结构不猜。
@@ -47527,6 +47903,7 @@ function ActiveInteractionCard({ request, bridge }) {
47527
47903
  message: ""
47528
47904
  } : request, {
47529
47905
  ...interactionLabels(request, t),
47906
+ ...localized ? { message: "" } : {},
47530
47907
  title: request.type === "form" && request.title ? request.title : ""
47531
47908
  });
47532
47909
  const preview = isAuthorize && typeof request.details === "object" && request.details !== null ? request.details["preview"] : void 0;
@@ -47609,9 +47986,9 @@ function ActiveInteractionCard({ request, bridge }) {
47609
47986
  /* @__PURE__ */ jsx("p", {
47610
47987
  "data-testid": "interaction-page-action",
47611
47988
  className: "text-sm text-ink",
47612
- children: t(`interaction.pageAction.${pageAction.action}`, { target: pageAction.name ?? t(`interaction.pageAction.role.${pageAction.action}`) })
47989
+ children: pageActionMessage(pageAction, t)
47613
47990
  }),
47614
- pageAction.value !== void 0 ? /* @__PURE__ */ jsx("p", {
47991
+ pageAction.value !== void 0 && pageAction.action !== "scroll" ? /* @__PURE__ */ jsx("p", {
47615
47992
  "data-testid": "interaction-page-action-value",
47616
47993
  className: "font-mono text-sm text-ink",
47617
47994
  children: pageAction.value
@@ -48132,7 +48509,7 @@ async function downloadArtifactFile(storage, chatRoot, file) {
48132
48509
  * presentation shell and accessible thread primitives.
48133
48510
  * @stable
48134
48511
  */
48135
- function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, renderer: rendererProp, dictationLang: dictationLangProp, surfaceRegistry, layout = "auto", sessionList = "auto", onOpenSettings, docked, onDock, onClose, onEngineReady }) {
48512
+ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, renderer: rendererProp, dictationLang: dictationLangProp, onDictationPermissionDenied, onCameraPermissionDenied, surfaceRegistry, layout = "auto", sessionList = "auto", onOpenSettings, docked, onDock, onClose, onEngineReady }) {
48136
48513
  const engine = useMemo(() => new ChatEngine(adapter, {
48137
48514
  ...config?.chatRoot ? { chatRoot: config.chatRoot } : {},
48138
48515
  ...config?.llm ? { llm: config.llm } : {},
@@ -48829,6 +49206,8 @@ function Chatbot({ adapter, config, locale: localeProp, theme: themeProp, render
48829
49206
  imageCapable: modelTakesImages
48830
49207
  },
48831
49208
  ...dictationLang !== "" ? { dictationLang } : {},
49209
+ ...onDictationPermissionDenied ? { onDictationPermissionDenied } : {},
49210
+ ...onCameraPermissionDenied ? { onCameraPermissionDenied } : {},
48832
49211
  ...composerModels && !noUsableModel ? { models: composerModels } : {},
48833
49212
  ...noUsableModel ? { noModel: { onConfigure: () => setNoModelGuideOpen(true) } } : {},
48834
49213
  ...config?.placeholder ? { placeholder: config.placeholder } : {}
@@ -48905,7 +49284,7 @@ function VercelPayloadPreview({ bridge }) {
48905
49284
  * Version of the published `@webskill/chatbot` package, injected at build time.
48906
49285
  * @stable
48907
49286
  */
48908
- const CHATBOT_VERSION = "0.13.0";
49287
+ const CHATBOT_VERSION = "0.15.0";
48909
49288
 
48910
49289
  //#endregion
48911
49290
  export { A2uiSurfaceHost, A2uiSurfaceSnapshotHost, CHATBOT_VERSION, ChatEngine, Chatbot, CompositeUiBridge, DEFAULT_RENDERER_CAPABILITIES, DEFAULT_RUNTIME_CONFIG_STORAGE_KEY, ErrorCard, InteractionCard, InterruptedBanner, OpenUiSurfaceHost, OpenUiSurfaceSnapshotHost, ResultBlockList, ResultBlocksPro, SkillBadges, SpecInteraction, VercelPayloadPreview, VercelSurfaceHost, VercelSurfaceSnapshotHost, chatbotDictionary, configureA2uiMarkdown, createLocalStorageRuntimeConfigStore, createMemoryRuntimeConfigStore, isLlmEntryUsable, pickUsableLlmEntry, probeA2uiAvailability, probeOpenUiAvailability, sandboxExecutorDeps, useT };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webskill/chatbot",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -26,7 +26,7 @@
26
26
  "@a2ui/web_core": "^0.10.5",
27
27
  "@lit/context": "^1.1.0",
28
28
  "@openuidev/react-lang": "0.2.8",
29
- "@webskill/sdk": "^0.16.0",
29
+ "@webskill/sdk": "^0.18.0",
30
30
  "react": ">=19.0.0",
31
31
  "react-dom": ">=19.0.0",
32
32
  "zod": "^3.25.0 || ^4.0.0"