@agent-native/core 0.102.1 → 0.103.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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +15 -0
- package/corpus/core/docs/content/local-file-mode.mdx +99 -489
- package/corpus/core/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
- package/corpus/core/docs/content/locales/ar-SA/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
- package/corpus/core/docs/content/locales/de-DE/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
- package/corpus/core/docs/content/locales/es-ES/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
- package/corpus/core/docs/content/locales/fr-FR/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
- package/corpus/core/docs/content/locales/hi-IN/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
- package/corpus/core/docs/content/locales/ja-JP/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
- package/corpus/core/docs/content/locales/ko-KR/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
- package/corpus/core/docs/content/locales/pt-BR/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
- package/corpus/core/docs/content/locales/zh-CN/template-content.mdx +9 -24
- package/corpus/core/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
- package/corpus/core/docs/content/locales/zh-TW/template-content.mdx +9 -24
- package/corpus/core/docs/content/template-content.mdx +26 -42
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/content-local.ts +86 -18
- package/corpus/core/src/cli/index.ts +2 -2
- package/corpus/core/src/cli/skills-content/content-skill.ts +36 -25
- package/corpus/core/src/cli/skills.ts +47 -13
- package/corpus/core/src/client/AgentPanel.tsx +7 -0
- package/corpus/core/src/client/AssistantChat.tsx +7 -0
- package/corpus/core/src/client/agent-chat-adapter.ts +7 -1
- package/corpus/core/src/client/chat/message-components.tsx +17 -3
- package/corpus/core/src/client/composer/ComposerPlusMenu.tsx +96 -6
- package/corpus/core/src/client/composer/asset-picker-url.ts +80 -0
- package/corpus/core/src/client/error-format.ts +24 -0
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +8 -1
- package/corpus/core/src/local-artifacts/index.ts +65 -0
- package/corpus/templates/assets/app/components/library/GenerationPresetsPanel.tsx +325 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +12 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +21 -0
- package/corpus/templates/assets/app/i18n-data.ts +215 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +19 -479
- package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id_.settings.tsx +446 -0
- package/corpus/templates/assets/app/routes/library.tsx +69 -0
- package/corpus/templates/assets/changelog/2026-07-15-fixed-top-level-image-picker-selections-returning-to-the-com.md +6 -0
- package/corpus/templates/clips/actions/disconnect-calendar.ts +57 -9
- package/corpus/templates/clips/actions/generate-workflow.ts +102 -59
- package/corpus/templates/clips/actions/get-recording-player-data.ts +64 -1
- package/corpus/templates/clips/actions/regenerate-summary.ts +7 -0
- package/corpus/templates/clips/actions/regenerate-title.ts +29 -11
- package/corpus/templates/clips/app/components/editor/editor-layout.tsx +34 -13
- package/corpus/templates/clips/app/components/editor/thumbnail-picker.tsx +87 -9
- package/corpus/templates/clips/app/components/editor/trim-handles.tsx +15 -0
- package/corpus/templates/clips/app/components/editor/waveform.tsx +21 -8
- package/corpus/templates/clips/app/components/library/folder-tree.tsx +52 -0
- package/corpus/templates/clips/app/components/player/settings-panel.tsx +116 -58
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +43 -37
- package/corpus/templates/clips/app/components/player/video-player.tsx +50 -2
- package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +22 -3
- package/corpus/templates/clips/app/i18n/ar-SA.ts +5 -2
- package/corpus/templates/clips/app/i18n/de-DE.ts +6 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +5 -2
- package/corpus/templates/clips/app/i18n/es-ES.ts +6 -2
- package/corpus/templates/clips/app/i18n/fr-FR.ts +5 -2
- package/corpus/templates/clips/app/i18n/hi-IN.ts +5 -2
- package/corpus/templates/clips/app/i18n/ja-JP.ts +6 -1
- package/corpus/templates/clips/app/i18n/ko-KR.ts +5 -2
- package/corpus/templates/clips/app/i18n/pt-BR.ts +6 -2
- package/corpus/templates/clips/app/i18n/zh-CN.ts +5 -2
- package/corpus/templates/clips/app/i18n/zh-TW.ts +5 -2
- package/corpus/templates/clips/app/lib/timestamp-mapping.ts +14 -0
- package/corpus/templates/clips/app/routes/_app.library._index.tsx +1 -2
- package/corpus/templates/clips/app/routes/_index.tsx +1 -2
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +98 -32
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +4 -1
- package/corpus/templates/clips/changelog/2026-07-15-ai-workflow-generation-now-stays-visible-avoids-duplicate-re.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-clip-playback-and-cta-controls-remain-interactive-through-ed.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-clip-titles-no-longer-describe-the-product-as-a-loom-alterna.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-description-regeneration-now-opens-the-agent-chat-so-progres.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-direct-recording-urls-now-respect-share-link-and-password-ac.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-disconnecting-a-calendar-now-clears-unrecorded-synced-meetin.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-folders-now-expose-rename-and-delete-actions-directly-in-the.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-recording-pages-now-explain-sign-in-access-and-avoid-showing.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-the-clips-extension-quick-actions-toolbar-can-be-moved-out-o.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-the-editor-now-skips-cut-sections-during-preview-and-keeps-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-thumbnail-capture-now-updates-previews-while-scrubbing-and-l.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-15-windows-permission-links-open-the-matching-settings-page.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +117 -2
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +27 -0
- package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -1
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +139 -101
- package/corpus/templates/clips/server/lib/recording-page-access.ts +35 -0
- package/corpus/templates/clips/server/lib/share-password.ts +6 -3
- package/corpus/templates/content/.agents/skills/content/SKILL.md +26 -39
- package/corpus/templates/content/AGENTS.md +25 -30
- package/corpus/templates/content/actions/_builder-docs-client.ts +23 -0
- package/corpus/templates/content/actions/_content-database-source-adapters.ts +21 -3
- package/corpus/templates/content/actions/_content-files.ts +338 -0
- package/corpus/templates/content/actions/_content-space-access.ts +135 -0
- package/corpus/templates/content/actions/_content-spaces.ts +640 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +90 -7
- package/corpus/templates/content/actions/_database-utils.ts +7 -1
- package/corpus/templates/content/actions/_local-file-documents.ts +0 -9
- package/corpus/templates/content/actions/_local-folder-source.ts +53 -0
- package/corpus/templates/content/actions/_property-utils.ts +9 -4
- package/corpus/templates/content/actions/add-database-item.ts +108 -65
- package/corpus/templates/content/actions/backfill-content-files.ts +22 -0
- package/corpus/templates/content/actions/connect-local-folder-source.ts +221 -0
- package/corpus/templates/content/actions/create-content-database.ts +129 -3
- package/corpus/templates/content/actions/create-document.ts +65 -55
- package/corpus/templates/content/actions/create-inline-content-database.ts +6 -1
- package/corpus/templates/content/actions/delete-content-database.ts +3 -0
- package/corpus/templates/content/actions/delete-document.ts +7 -11
- package/corpus/templates/content/actions/disconnect-local-folder-source.ts +175 -0
- package/corpus/templates/content/actions/duplicate-database-item.ts +10 -0
- package/corpus/templates/content/actions/duplicate-database-items.ts +13 -0
- package/corpus/templates/content/actions/edit-document.ts +2 -28
- package/corpus/templates/content/actions/ensure-content-spaces.ts +26 -0
- package/corpus/templates/content/actions/export-content-source.ts +47 -9
- package/corpus/templates/content/actions/get-document.ts +0 -14
- package/corpus/templates/content/actions/import-content-source.ts +43 -3
- package/corpus/templates/content/actions/list-content-spaces.ts +103 -0
- package/corpus/templates/content/actions/list-documents.ts +12 -9
- package/corpus/templates/content/actions/move-document.ts +3 -14
- package/corpus/templates/content/actions/pull-document.ts +0 -23
- package/corpus/templates/content/actions/remove-local-file-source.ts +50 -29
- package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +442 -0
- package/corpus/templates/content/actions/reveal-local-source-file.ts +6 -17
- package/corpus/templates/content/actions/search-documents.ts +0 -34
- package/corpus/templates/content/actions/share-local-file-document.ts +25 -1
- package/corpus/templates/content/actions/submit-content-database-form.ts +12 -0
- package/corpus/templates/content/actions/sync-local-folder-source.ts +731 -0
- package/corpus/templates/content/actions/sync-manifest-local-folder-source.ts +123 -0
- package/corpus/templates/content/actions/update-content-database-view.ts +10 -1
- package/corpus/templates/content/actions/update-document.ts +0 -15
- package/corpus/templates/content/actions/view-screen.ts +2 -50
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +76 -62
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +70 -22
- package/corpus/templates/content/app/components/editor/database/grouping.ts +2 -1
- package/corpus/templates/content/app/components/editor/database/settings.tsx +23 -20
- package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +330 -0
- package/corpus/templates/content/app/components/editor/database/types.ts +1 -0
- package/corpus/templates/content/app/components/editor/database/view-config.ts +3 -1
- package/corpus/templates/content/app/components/layout/Layout.tsx +25 -1
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +209 -113
- package/corpus/templates/content/app/components/sidebar/select-content-space.ts +31 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +16 -0
- package/corpus/templates/content/app/hooks/use-content-spaces.ts +39 -0
- package/corpus/templates/content/app/i18n/zh-TW.ts +11 -0
- package/corpus/templates/content/app/i18n-data.ts +75 -0
- package/corpus/templates/content/app/routes/_app.local-files.tsx +174 -65
- package/corpus/templates/content/changelog/2026-07-16-content-callouts-can-now-be-reviewed-and-published-to-builder.md +6 -0
- package/corpus/templates/content/changelog/2026-07-16-heading-5-and-heading-6-are-available-from-the-editor-slash-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-16-organize-every-workspace-through-a-customizable-files-databa.md +6 -0
- package/corpus/templates/content/parity/matrix.md +2 -1
- package/corpus/templates/content/parity/matrix.ts +37 -1
- package/corpus/templates/content/scripts/dev-database.mjs +7 -5
- package/corpus/templates/content/server/db/schema.ts +113 -40
- package/corpus/templates/content/server/lib/document-context.ts +15 -1
- package/corpus/templates/content/server/lib/notion-sync.ts +6 -0
- package/corpus/templates/content/server/plugins/db.ts +153 -0
- package/corpus/templates/content/shared/api.ts +17 -3
- package/corpus/templates/content/shared/builder-mdx.ts +163 -16
- package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +59 -0
- package/corpus/templates/design/AGENTS.md +6 -1
- package/corpus/templates/design/actions/create-design-from-template.ts +19 -7
- package/corpus/templates/design/actions/list-design-templates.ts +69 -21
- package/corpus/templates/design/actions/navigate.ts +2 -2
- package/corpus/templates/design/actions/view-screen.ts +15 -2
- package/corpus/templates/design/agent-native.app-skill.json +5 -0
- package/corpus/templates/design/app/components/design/design-canvas/external-preview.ts +7 -3
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +281 -56
- package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +14 -2
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +3 -3
- package/corpus/templates/design/app/i18n-data.ts +21 -2
- package/corpus/templates/design/app/i18n-template-feature.ts +218 -10
- package/corpus/templates/design/app/pages/Index.tsx +195 -24
- package/corpus/templates/design/app/pages/Templates.tsx +144 -55
- package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +12 -1
- package/corpus/templates/design/changelog/2026-07-14-templates-now-appear-in-the-new-design-picker-with-previews-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-15-prototype-print-and-download-buttons-now-work-in-preview.md +6 -0
- package/corpus/templates/design/changelog/2026-07-15-templates-now-open-immediately-after-copying-even-while-the-.md +6 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/mail/AGENTS.md +23 -18
- package/corpus/templates/mail/actions/bulk-archive.ts +32 -28
- package/corpus/templates/mail/actions/mark-read.ts +131 -20
- package/corpus/templates/mail/actions/mark-thread-read.ts +1 -1
- package/corpus/templates/mail/actions/move-email.ts +30 -24
- package/corpus/templates/mail/actions/send-email.ts +73 -68
- package/corpus/templates/mail/changelog/2026-07-15-unread-inbox-cleanup-now-completes-reliably.md +6 -0
- package/corpus/templates/mail/server/handlers/emails.ts +218 -209
- package/corpus/templates/mail/server/lib/bulk-mark-read.ts +30 -0
- package/corpus/templates/mail/server/lib/email-state.ts +198 -113
- package/corpus/templates/mail/server/lib/google-auth.ts +218 -25
- package/corpus/templates/mail/server/lib/jobs.ts +69 -74
- package/corpus/templates/mail/server/lib/local-email-store.ts +265 -0
- package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
- package/corpus/templates/slides/actions/extract-pdf.ts +1 -1
- package/corpus/templates/slides/actions/import-file.ts +4 -1
- package/corpus/templates/slides/app/components/editor/ImageGenPanel.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/ImageSearchPanel.tsx +9 -12
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +29 -21
- package/corpus/templates/slides/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/slides/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
- package/corpus/templates/slides/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/slides/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/slides/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/slides/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/slides/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/slides/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/slides/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/slides/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/slides/changelog/2026-07-15-image-search-and-generation-controls-now-use-the-registered-.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-15-pdf-deck-references-can-be-imported-for-slide-inspiration-ag.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-15-stable-style-inspector-dock.md +6 -0
- package/dist/cli/content-local.d.ts +1 -0
- package/dist/cli/content-local.d.ts.map +1 -1
- package/dist/cli/content-local.js +75 -17
- package/dist/cli/content-local.js.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/skills-content/content-skill.d.ts +1 -1
- package/dist/cli/skills-content/content-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/content-skill.js +36 -25
- package/dist/cli/skills-content/content-skill.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +47 -14
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +5 -0
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +7 -0
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +8 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/message-components.d.ts +4 -0
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +10 -3
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.d.ts.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.js +58 -6
- package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/client/composer/asset-picker-url.d.ts +16 -0
- package/dist/client/composer/asset-picker-url.d.ts.map +1 -0
- package/dist/client/composer/asset-picker-url.js +64 -0
- package/dist/client/composer/asset-picker-url.js.map +1 -0
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +17 -0
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +6 -1
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/local-artifacts/index.d.ts +10 -0
- package/dist/local-artifacts/index.d.ts.map +1 -1
- package/dist/local-artifacts/index.js +39 -0
- package/dist/local-artifacts/index.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/docs/content/local-file-mode.mdx +99 -489
- package/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
- package/docs/content/locales/ar-SA/template-content.mdx +10 -24
- package/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
- package/docs/content/locales/de-DE/template-content.mdx +10 -24
- package/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
- package/docs/content/locales/es-ES/template-content.mdx +10 -24
- package/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
- package/docs/content/locales/fr-FR/template-content.mdx +10 -24
- package/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
- package/docs/content/locales/hi-IN/template-content.mdx +10 -24
- package/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
- package/docs/content/locales/ja-JP/template-content.mdx +10 -24
- package/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
- package/docs/content/locales/ko-KR/template-content.mdx +10 -24
- package/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
- package/docs/content/locales/pt-BR/template-content.mdx +10 -24
- package/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
- package/docs/content/locales/zh-CN/template-content.mdx +9 -24
- package/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
- package/docs/content/locales/zh-TW/template-content.mdx +9 -24
- package/docs/content/template-content.mdx +26 -42
- package/package.json +1 -1
- package/corpus/templates/assets/app/components/library/EditLibraryDialog.tsx +0 -126
|
@@ -13,6 +13,7 @@ export interface TrimHandlesProps {
|
|
|
13
13
|
/** Fires when the user releases the mouse after drag. */
|
|
14
14
|
onCommit?: (value: { startMs: number; endMs: number }) => void;
|
|
15
15
|
durationMs: number;
|
|
16
|
+
splitPoints?: number[];
|
|
16
17
|
className?: string;
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -44,6 +45,7 @@ export function TrimHandles({
|
|
|
44
45
|
onChange,
|
|
45
46
|
onCommit,
|
|
46
47
|
durationMs,
|
|
48
|
+
splitPoints = [],
|
|
47
49
|
className,
|
|
48
50
|
}: TrimHandlesProps) {
|
|
49
51
|
const rootRef = useRef<HTMLDivElement | null>(null);
|
|
@@ -111,6 +113,9 @@ export function TrimHandles({
|
|
|
111
113
|
|
|
112
114
|
const startX = (value.startMs / Math.max(durationMs, 1)) * width;
|
|
113
115
|
const endX = (value.endMs / Math.max(durationMs, 1)) * width;
|
|
116
|
+
const visibleSplitPoints = splitPoints.filter(
|
|
117
|
+
(ms) => ms > value.startMs && ms < value.endMs, // i18n-ignore numeric range predicate, not visible UI copy
|
|
118
|
+
);
|
|
114
119
|
|
|
115
120
|
return (
|
|
116
121
|
<div
|
|
@@ -133,6 +138,16 @@ export function TrimHandles({
|
|
|
133
138
|
<div className="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-full bg-foreground text-background text-[10px] px-1.5 py-0.5 rounded font-mono whitespace-nowrap">
|
|
134
139
|
{formatMs(value.endMs - value.startMs)}
|
|
135
140
|
</div>
|
|
141
|
+
{visibleSplitPoints.map((splitMs) => (
|
|
142
|
+
<div
|
|
143
|
+
key={splitMs}
|
|
144
|
+
className="absolute top-0 h-full w-0.5 -translate-x-1/2 bg-rose-500/95"
|
|
145
|
+
style={{
|
|
146
|
+
left: `${((splitMs - value.startMs) / Math.max(value.endMs - value.startMs, 1)) * 100}%`,
|
|
147
|
+
}}
|
|
148
|
+
aria-hidden="true"
|
|
149
|
+
/>
|
|
150
|
+
))}
|
|
136
151
|
</div>
|
|
137
152
|
|
|
138
153
|
{/* Left handle */}
|
|
@@ -18,6 +18,8 @@ export interface WaveformProps {
|
|
|
18
18
|
durationMs: number;
|
|
19
19
|
/** Excluded ranges (original time) — drawn as striped overlays. */
|
|
20
20
|
excludedRanges?: Array<{ startMs: number; endMs: number }>;
|
|
21
|
+
/** Split markers (original time) — drawn over the active selection. */
|
|
22
|
+
splitPoints?: number[];
|
|
21
23
|
/** Optional selection range (original time) highlighted in brand color. */
|
|
22
24
|
selectionRange?: { startMs: number; endMs: number } | null;
|
|
23
25
|
/** Transcript-backed activity ranges used when browser audio decoding fails. */
|
|
@@ -87,6 +89,7 @@ export function Waveform({
|
|
|
87
89
|
playheadMs,
|
|
88
90
|
durationMs,
|
|
89
91
|
excludedRanges,
|
|
92
|
+
splitPoints = [],
|
|
90
93
|
selectionRange,
|
|
91
94
|
activityRanges = [],
|
|
92
95
|
onSeek,
|
|
@@ -220,19 +223,28 @@ export function Waveform({
|
|
|
220
223
|
|
|
221
224
|
// Selection overlay
|
|
222
225
|
if (selectionRange) {
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const xEnd =
|
|
228
|
-
(Math.max(selectionRange.startMs, selectionRange.endMs) /
|
|
229
|
-
Math.max(durationMs, 1)) *
|
|
230
|
-
totalWidth;
|
|
226
|
+
const startMs = Math.min(selectionRange.startMs, selectionRange.endMs);
|
|
227
|
+
const endMs = Math.max(selectionRange.startMs, selectionRange.endMs);
|
|
228
|
+
const xStart = (startMs / Math.max(durationMs, 1)) * totalWidth;
|
|
229
|
+
const xEnd = (endMs / Math.max(durationMs, 1)) * totalWidth;
|
|
231
230
|
ctx.fillStyle = getBrandColorAlpha(0.28);
|
|
232
231
|
ctx.fillRect(xStart, 0, xEnd - xStart, height);
|
|
233
232
|
ctx.strokeStyle = getBrandColor();
|
|
234
233
|
ctx.lineWidth = 1;
|
|
235
234
|
ctx.strokeRect(xStart + 0.5, 0.5, xEnd - xStart - 1, height - 1);
|
|
235
|
+
|
|
236
|
+
// Keep split markers visible on the selected track as well as on the
|
|
237
|
+
// ruler so a split is visibly actionable within the selection.
|
|
238
|
+
for (const splitMs of splitPoints) {
|
|
239
|
+
if (splitMs <= startMs || splitMs >= endMs) continue;
|
|
240
|
+
const splitX = (splitMs / Math.max(durationMs, 1)) * totalWidth;
|
|
241
|
+
ctx.strokeStyle = "rgba(244, 63, 94, 0.95)";
|
|
242
|
+
ctx.lineWidth = 2;
|
|
243
|
+
ctx.beginPath();
|
|
244
|
+
ctx.moveTo(splitX, 0);
|
|
245
|
+
ctx.lineTo(splitX, height);
|
|
246
|
+
ctx.stroke();
|
|
247
|
+
}
|
|
236
248
|
}
|
|
237
249
|
}, [
|
|
238
250
|
peaks,
|
|
@@ -240,6 +252,7 @@ export function Waveform({
|
|
|
240
252
|
height,
|
|
241
253
|
excludedRanges,
|
|
242
254
|
selectionRange,
|
|
255
|
+
splitPoints,
|
|
243
256
|
durationMs,
|
|
244
257
|
activityRanges,
|
|
245
258
|
]);
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
IconFolderPlus,
|
|
7
7
|
IconTrash,
|
|
8
8
|
IconEdit,
|
|
9
|
+
IconDots,
|
|
9
10
|
} from "@tabler/icons-react";
|
|
10
11
|
import { useMemo, useState } from "react";
|
|
11
12
|
import { NavLink } from "react-router";
|
|
@@ -33,6 +34,13 @@ import {
|
|
|
33
34
|
ContextMenuSeparator,
|
|
34
35
|
ContextMenuTrigger,
|
|
35
36
|
} from "@/components/ui/context-menu";
|
|
37
|
+
import {
|
|
38
|
+
DropdownMenu,
|
|
39
|
+
DropdownMenuContent,
|
|
40
|
+
DropdownMenuItem,
|
|
41
|
+
DropdownMenuSeparator,
|
|
42
|
+
DropdownMenuTrigger,
|
|
43
|
+
} from "@/components/ui/dropdown-menu";
|
|
36
44
|
import {
|
|
37
45
|
useCreateFolder,
|
|
38
46
|
useDeleteFolder,
|
|
@@ -186,6 +194,50 @@ function FolderItem({
|
|
|
186
194
|
{node.name}
|
|
187
195
|
</span>
|
|
188
196
|
</NavLink>
|
|
197
|
+
<DropdownMenu>
|
|
198
|
+
<DropdownMenuTrigger asChild>
|
|
199
|
+
<button
|
|
200
|
+
type="button"
|
|
201
|
+
aria-label={`${node.name}: ${t("root.commandActions")}`}
|
|
202
|
+
title={`${node.name}: ${t("root.commandActions")}`}
|
|
203
|
+
className="rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:bg-accent hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100"
|
|
204
|
+
>
|
|
205
|
+
<IconDots className="h-3.5 w-3.5" />
|
|
206
|
+
</button>
|
|
207
|
+
</DropdownMenuTrigger>
|
|
208
|
+
<DropdownMenuContent align="start" side="right">
|
|
209
|
+
<DropdownMenuItem
|
|
210
|
+
onSelect={() => {
|
|
211
|
+
setTimeout(() => {
|
|
212
|
+
setRenameValue(node.name);
|
|
213
|
+
setRenameOpen(true);
|
|
214
|
+
}, 0);
|
|
215
|
+
}}
|
|
216
|
+
>
|
|
217
|
+
<IconEdit className="h-3.5 w-3.5 me-2" />{" "}
|
|
218
|
+
{t("folderTree.rename")}
|
|
219
|
+
</DropdownMenuItem>
|
|
220
|
+
<DropdownMenuItem
|
|
221
|
+
onSelect={() => {
|
|
222
|
+
setTimeout(() => {
|
|
223
|
+
setNewValue("");
|
|
224
|
+
setNewOpen(true);
|
|
225
|
+
}, 0);
|
|
226
|
+
}}
|
|
227
|
+
>
|
|
228
|
+
<IconFolderPlus className="h-3.5 w-3.5 me-2" />{" "}
|
|
229
|
+
{t("folderTree.newSubfolder")}
|
|
230
|
+
</DropdownMenuItem>
|
|
231
|
+
<DropdownMenuSeparator />
|
|
232
|
+
<DropdownMenuItem
|
|
233
|
+
onSelect={() => setTimeout(() => setConfirmDelete(true), 0)}
|
|
234
|
+
className="text-destructive"
|
|
235
|
+
>
|
|
236
|
+
<IconTrash className="h-3.5 w-3.5 me-2" />{" "}
|
|
237
|
+
{t("folderTree.delete")}
|
|
238
|
+
</DropdownMenuItem>
|
|
239
|
+
</DropdownMenuContent>
|
|
240
|
+
</DropdownMenu>
|
|
189
241
|
</div>
|
|
190
242
|
</ContextMenuTrigger>
|
|
191
243
|
<ContextMenuContent>
|
|
@@ -156,7 +156,7 @@ export function SettingsPanel(props: SettingsPanelProps) {
|
|
|
156
156
|
<Label className="text-xs">
|
|
157
157
|
{t("playerSettings.passwordProtection")}
|
|
158
158
|
</Label>
|
|
159
|
-
<div className="
|
|
159
|
+
<div className="mt-1 space-y-2">
|
|
160
160
|
<Input
|
|
161
161
|
type="text"
|
|
162
162
|
value={password}
|
|
@@ -166,18 +166,36 @@ export function SettingsPanel(props: SettingsPanelProps) {
|
|
|
166
166
|
? t("playerSettings.passwordSetPlaceholder")
|
|
167
167
|
: t("playerSettings.noPasswordPlaceholder")
|
|
168
168
|
}
|
|
169
|
-
className="flex-1"
|
|
170
169
|
/>
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
170
|
+
{password.length > 0 && !password.trim() ? (
|
|
171
|
+
<p className="text-xs text-muted-foreground">
|
|
172
|
+
{t("playerSettings.passwordWhitespaceOnly")}
|
|
173
|
+
</p>
|
|
174
|
+
) : null}
|
|
175
|
+
<div className="flex gap-2">
|
|
176
|
+
<Button
|
|
177
|
+
variant="outline"
|
|
178
|
+
onClick={() => {
|
|
179
|
+
patch({ password: password.trim() });
|
|
180
|
+
setPassword("");
|
|
181
|
+
}}
|
|
182
|
+
disabled={update.isPending || !password.trim()}
|
|
183
|
+
>
|
|
184
|
+
{t("common.save")}
|
|
185
|
+
</Button>
|
|
186
|
+
{recording.hasPassword ? (
|
|
187
|
+
<Button
|
|
188
|
+
variant="outline"
|
|
189
|
+
onClick={() => {
|
|
190
|
+
patch({ password: null });
|
|
191
|
+
setPassword("");
|
|
192
|
+
}}
|
|
193
|
+
disabled={update.isPending}
|
|
194
|
+
>
|
|
195
|
+
{t("playerSettings.removePassword")}
|
|
196
|
+
</Button>
|
|
197
|
+
) : null}
|
|
198
|
+
</div>
|
|
181
199
|
</div>
|
|
182
200
|
</div>
|
|
183
201
|
|
|
@@ -361,6 +379,7 @@ function CtaEditor({
|
|
|
361
379
|
// editing this card, so an agent edit to the CTA shows up live. `editing`
|
|
362
380
|
// flips true while focus is anywhere inside the card.
|
|
363
381
|
const editing = useRef(false);
|
|
382
|
+
const [collapsed, setCollapsed] = useState(true);
|
|
364
383
|
const [label, setLabel] = useReconciledState(cta.label, {
|
|
365
384
|
active: editing.current,
|
|
366
385
|
});
|
|
@@ -387,52 +406,91 @@ function CtaEditor({
|
|
|
387
406
|
}
|
|
388
407
|
}}
|
|
389
408
|
>
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
409
|
+
{collapsed ? (
|
|
410
|
+
<div className="flex items-center gap-2">
|
|
411
|
+
<div className="min-w-0 flex-1">
|
|
412
|
+
<p className="truncate text-sm font-medium">{cta.label}</p>
|
|
413
|
+
<p className="text-xs text-muted-foreground">
|
|
414
|
+
{cta.placement === "throughout"
|
|
415
|
+
? t("playerSettings.placementThroughout")
|
|
416
|
+
: t("playerSettings.placementEnd")}
|
|
417
|
+
</p>
|
|
418
|
+
</div>
|
|
419
|
+
<Button
|
|
420
|
+
type="button"
|
|
421
|
+
size="sm"
|
|
422
|
+
variant="ghost"
|
|
423
|
+
title={t("recordingPage.edit")}
|
|
424
|
+
onClick={() => setCollapsed(false)}
|
|
425
|
+
>
|
|
426
|
+
{t("recordingPage.edit")}
|
|
427
|
+
</Button>
|
|
428
|
+
<Button type="button" size="sm" variant="ghost" onClick={onDelete}>
|
|
429
|
+
{t("playerSettings.delete")}
|
|
430
|
+
</Button>
|
|
431
|
+
</div>
|
|
432
|
+
) : (
|
|
433
|
+
<>
|
|
434
|
+
<Input
|
|
435
|
+
value={label}
|
|
436
|
+
onChange={(e) => setLabel(e.target.value)}
|
|
437
|
+
placeholder={t("playerSettings.buttonLabelPlaceholder")}
|
|
438
|
+
/>
|
|
439
|
+
<Input
|
|
440
|
+
value={url}
|
|
441
|
+
onChange={(e) => setUrl(e.target.value)}
|
|
442
|
+
placeholder="https://…"
|
|
443
|
+
/>
|
|
444
|
+
<div className="flex gap-2">
|
|
445
|
+
<input
|
|
446
|
+
type="color"
|
|
447
|
+
value={color}
|
|
448
|
+
onChange={(e) => setColor(e.target.value)}
|
|
449
|
+
className="h-9 w-12 rounded cursor-pointer border border-border"
|
|
450
|
+
/>
|
|
451
|
+
<Select
|
|
452
|
+
value={placement}
|
|
453
|
+
onValueChange={(v) => setPlacement(v as "end" | "throughout")}
|
|
454
|
+
>
|
|
455
|
+
<SelectTrigger className="flex-1">
|
|
456
|
+
<SelectValue />
|
|
457
|
+
</SelectTrigger>
|
|
458
|
+
<SelectContent>
|
|
459
|
+
<SelectItem value="throughout">
|
|
460
|
+
{t("playerSettings.placementThroughout")}
|
|
461
|
+
</SelectItem>
|
|
462
|
+
<SelectItem value="end">
|
|
463
|
+
{t("playerSettings.placementEnd")}
|
|
464
|
+
</SelectItem>
|
|
465
|
+
</SelectContent>
|
|
466
|
+
</Select>
|
|
467
|
+
</div>
|
|
468
|
+
<div className="flex gap-2">
|
|
469
|
+
<Button
|
|
470
|
+
type="button"
|
|
471
|
+
size="sm"
|
|
472
|
+
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
|
473
|
+
onClick={() => {
|
|
474
|
+
onSave({ label, url, color, placement });
|
|
475
|
+
setCollapsed(true);
|
|
476
|
+
}}
|
|
477
|
+
>
|
|
478
|
+
{t("common.save")}
|
|
479
|
+
</Button>
|
|
480
|
+
<Button
|
|
481
|
+
type="button"
|
|
482
|
+
size="sm"
|
|
483
|
+
variant="ghost"
|
|
484
|
+
onClick={() => setCollapsed(true)}
|
|
485
|
+
>
|
|
486
|
+
{t("recordingPage.done")}
|
|
487
|
+
</Button>
|
|
488
|
+
<Button type="button" size="sm" variant="ghost" onClick={onDelete}>
|
|
489
|
+
{t("playerSettings.delete")}
|
|
490
|
+
</Button>
|
|
491
|
+
</div>
|
|
492
|
+
</>
|
|
493
|
+
)}
|
|
436
494
|
</div>
|
|
437
495
|
);
|
|
438
496
|
}
|
|
@@ -380,49 +380,55 @@ function LinkTab({
|
|
|
380
380
|
)}
|
|
381
381
|
|
|
382
382
|
<CopyField
|
|
383
|
-
label={
|
|
383
|
+
label={
|
|
384
|
+
isPublic
|
|
385
|
+
? t("shareDialog.shareLink")
|
|
386
|
+
: t("shareDialog.shareWithHumans")
|
|
387
|
+
}
|
|
384
388
|
value={shareUrl}
|
|
385
389
|
disabled={
|
|
386
390
|
visibilityPending || !sharesLoaded || (!isPublic && canManage)
|
|
387
391
|
}
|
|
388
392
|
/>
|
|
389
393
|
|
|
390
|
-
|
|
391
|
-
<
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
<
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
394
|
+
{!isPublic ? (
|
|
395
|
+
<div className="space-y-2">
|
|
396
|
+
<CopyField
|
|
397
|
+
label={t("shareDialog.shareWithAgents")}
|
|
398
|
+
value={agentLink}
|
|
399
|
+
disabled={agentShareDisabled}
|
|
400
|
+
/>
|
|
401
|
+
{sharesLoaded ? (
|
|
402
|
+
<>
|
|
403
|
+
<p className="text-xs text-muted-foreground">
|
|
404
|
+
{t("shareDialog.agentTokenDescription")}
|
|
405
|
+
</p>
|
|
406
|
+
{agentLinkError ? (
|
|
407
|
+
<div className="flex items-center justify-between gap-2 rounded-md border border-border bg-muted/30 px-3 py-2">
|
|
408
|
+
<p className="text-xs text-muted-foreground">
|
|
409
|
+
{t("shareDialog.agentLinkUnavailable")}
|
|
410
|
+
</p>
|
|
411
|
+
<Button
|
|
412
|
+
type="button"
|
|
413
|
+
variant="ghost"
|
|
414
|
+
size="sm"
|
|
415
|
+
className="h-7"
|
|
416
|
+
onClick={() => void loadAgentContextUrl()}
|
|
417
|
+
disabled={createAgentLink.isPending}
|
|
418
|
+
>
|
|
419
|
+
{t("shareDialog.retryAgentLink")}
|
|
420
|
+
</Button>
|
|
421
|
+
</div>
|
|
422
|
+
) : null}
|
|
423
|
+
<CopyField
|
|
424
|
+
label={t("shareDialog.copyAgentPrompt")}
|
|
425
|
+
value={agentPrompt}
|
|
426
|
+
disabled={agentShareDisabled}
|
|
427
|
+
/>
|
|
428
|
+
</>
|
|
429
|
+
) : null}
|
|
430
|
+
</div>
|
|
431
|
+
) : null}
|
|
426
432
|
|
|
427
433
|
{sharesLoaded && !isPublic && canManage ? (
|
|
428
434
|
<MakePublicCard
|
|
@@ -592,6 +592,14 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
592
592
|
pauseVideo();
|
|
593
593
|
return;
|
|
594
594
|
}
|
|
595
|
+
if (v.ended) {
|
|
596
|
+
try {
|
|
597
|
+
v.currentTime = 0;
|
|
598
|
+
setCurrentMs(0);
|
|
599
|
+
} catch {
|
|
600
|
+
// Let the normal play attempt report a media error if the seek fails.
|
|
601
|
+
}
|
|
602
|
+
}
|
|
595
603
|
requestPlay();
|
|
596
604
|
}, [isPlaying, pauseVideo, requestPlay]);
|
|
597
605
|
|
|
@@ -671,7 +679,10 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
671
679
|
(isPlaying || playAttemptPendingRef.current || !v.paused),
|
|
672
680
|
);
|
|
673
681
|
|
|
674
|
-
if (v)
|
|
682
|
+
if (v) {
|
|
683
|
+
v.defaultPlaybackRate = nextSpeed;
|
|
684
|
+
v.playbackRate = nextSpeed;
|
|
685
|
+
}
|
|
675
686
|
setSpeed(nextSpeed);
|
|
676
687
|
savePlaybackSpeedPreference(nextSpeed);
|
|
677
688
|
onSpeedChange?.(nextSpeed);
|
|
@@ -770,6 +781,7 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
770
781
|
const v = videoRef.current;
|
|
771
782
|
if (!v) return;
|
|
772
783
|
const initialSpeed = readPlaybackSpeedPreference(defaultSpeed);
|
|
784
|
+
v.defaultPlaybackRate = initialSpeed;
|
|
773
785
|
v.playbackRate = initialSpeed;
|
|
774
786
|
setSpeed(initialSpeed);
|
|
775
787
|
onSpeedChange?.(initialSpeed);
|
|
@@ -1423,6 +1435,21 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
1423
1435
|
</div>
|
|
1424
1436
|
)}
|
|
1425
1437
|
|
|
1438
|
+
{thumbnailUrl &&
|
|
1439
|
+
!autoPlay &&
|
|
1440
|
+
!hasPlaybackStarted &&
|
|
1441
|
+
(!startMs || startMs <= 0) ? (
|
|
1442
|
+
<img
|
|
1443
|
+
src={resolveLocalUrl(thumbnailUrl)}
|
|
1444
|
+
alt=""
|
|
1445
|
+
aria-hidden="true"
|
|
1446
|
+
className={cn(
|
|
1447
|
+
"pointer-events-none absolute inset-0 z-[1] h-full w-full",
|
|
1448
|
+
cover ? "object-cover" : "object-contain",
|
|
1449
|
+
)}
|
|
1450
|
+
/>
|
|
1451
|
+
) : null}
|
|
1452
|
+
|
|
1426
1453
|
{centerOverlayMode ? (
|
|
1427
1454
|
<CenterPlaybackOverlay
|
|
1428
1455
|
mode={centerOverlayMode}
|
|
@@ -1462,7 +1489,7 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
1462
1489
|
|
|
1463
1490
|
{/* Floating CTA (throughout placement) */}
|
|
1464
1491
|
{showThroughoutCta ? (
|
|
1465
|
-
<div data-player-ui className="absolute bottom-16 right-4 z-
|
|
1492
|
+
<div data-player-ui className="absolute bottom-16 right-4 z-30">
|
|
1466
1493
|
<CtaButton
|
|
1467
1494
|
cta={cta!}
|
|
1468
1495
|
onClick={() => onCtaClick?.(cta!.id)}
|
|
@@ -1484,6 +1511,27 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
1484
1511
|
onClick={() => onCtaClick?.(cta!.id)}
|
|
1485
1512
|
large
|
|
1486
1513
|
/>
|
|
1514
|
+
<button
|
|
1515
|
+
type="button"
|
|
1516
|
+
data-player-ui
|
|
1517
|
+
aria-label={t("videoPlayer.playClip")}
|
|
1518
|
+
onClick={(e) => {
|
|
1519
|
+
e.stopPropagation();
|
|
1520
|
+
const v = videoRef.current;
|
|
1521
|
+
if (!v) return;
|
|
1522
|
+
try {
|
|
1523
|
+
v.currentTime = 0;
|
|
1524
|
+
setCurrentMs(0);
|
|
1525
|
+
} catch {
|
|
1526
|
+
// The regular play path will surface any media error.
|
|
1527
|
+
}
|
|
1528
|
+
requestPlay();
|
|
1529
|
+
}}
|
|
1530
|
+
className="pointer-events-auto inline-flex items-center gap-2 rounded-md border border-white/30 bg-white/10 px-3 py-2 text-sm font-medium text-white transition-colors hover:bg-white/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white"
|
|
1531
|
+
>
|
|
1532
|
+
<IconPlayerPlay className="h-4 w-4 fill-current" />
|
|
1533
|
+
{t("videoPlayer.playClip")}
|
|
1534
|
+
</button>
|
|
1487
1535
|
</div>
|
|
1488
1536
|
</div>
|
|
1489
1537
|
) : null}
|
|
@@ -41,6 +41,26 @@ function stopStream(stream: MediaStream | null): void {
|
|
|
41
41
|
|
|
42
42
|
type MicrophonePermissionState = PermissionState | "unknown";
|
|
43
43
|
|
|
44
|
+
function isDesktopShell(): boolean {
|
|
45
|
+
if (typeof window === "undefined") return false;
|
|
46
|
+
const w = window as typeof window & {
|
|
47
|
+
electronAPI?: unknown;
|
|
48
|
+
__TAURI_INTERNALS__?: unknown;
|
|
49
|
+
__TAURI__?: unknown;
|
|
50
|
+
};
|
|
51
|
+
if (w.electronAPI || w.__TAURI_INTERNALS__ || w.__TAURI__) return true;
|
|
52
|
+
return (
|
|
53
|
+
typeof navigator !== "undefined" && /Electron/i.test(navigator.userAgent)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function micBlockedMessage(): string {
|
|
58
|
+
if (isDesktopShell()) {
|
|
59
|
+
return "Microphone access is blocked for this app. Enable the microphone for the app in your system Privacy settings, then reopen the recorder.";
|
|
60
|
+
}
|
|
61
|
+
return "Your browser has blocked microphone access for this site, so it won't prompt. Allow the microphone in this site's settings, then reload.";
|
|
62
|
+
}
|
|
63
|
+
|
|
44
64
|
function isMicrophoneBlockedByPolicy(): boolean {
|
|
45
65
|
const policy =
|
|
46
66
|
(
|
|
@@ -96,7 +116,7 @@ export async function friendlyMicError(err: unknown): Promise<string> {
|
|
|
96
116
|
return "Microphone prompts require HTTPS or localhost. Open this app on localhost or an HTTPS URL, then try again.";
|
|
97
117
|
}
|
|
98
118
|
if (permissionState === "denied") {
|
|
99
|
-
return
|
|
119
|
+
return micBlockedMessage();
|
|
100
120
|
}
|
|
101
121
|
if (/NotAllowedError|Permission denied|denied|blocked/i.test(combined)) {
|
|
102
122
|
return "The browser or operating system denied microphone access. Check this site's microphone setting and your system privacy settings for this browser, then reload.";
|
|
@@ -328,8 +348,7 @@ export function MicrophoneVisualizer({
|
|
|
328
348
|
const permissionState = await getMicrophonePermissionState();
|
|
329
349
|
if (runIdRef.current !== runId) return;
|
|
330
350
|
if (permissionState === "denied") {
|
|
331
|
-
const message =
|
|
332
|
-
"Brave already has Microphone set to Block for this site, so it will not show the popup. Click the lock/tune icon in the address bar → Site settings → Microphone → Allow, then reload.";
|
|
351
|
+
const message = micBlockedMessage();
|
|
333
352
|
setError(message);
|
|
334
353
|
setStatus("error");
|
|
335
354
|
onStatusChange?.("error", { error: message });
|
|
@@ -455,6 +455,8 @@ const messages = {
|
|
|
455
455
|
retryAgentLink: "إعادة المحاولة",
|
|
456
456
|
gifPreview: "معاينة GIF",
|
|
457
457
|
openPlayer: "مشغل مفتوح",
|
|
458
|
+
chooseFile: "اختيار ملف",
|
|
459
|
+
remove: "إزالة",
|
|
458
460
|
downloadMp4: "تحميل MP4",
|
|
459
461
|
embedsNeedPublic: "التضمينات تحتاج إلى مقطع عام",
|
|
460
462
|
embedPublicDescription:
|
|
@@ -1225,9 +1227,10 @@ const messages = {
|
|
|
1225
1227
|
visibilityOrg: "Organization (مترجم)",
|
|
1226
1228
|
visibilityPublic: "Public (مترجم)",
|
|
1227
1229
|
passwordProtection: "Password protection (مترجم)",
|
|
1228
|
-
passwordSetPlaceholder:
|
|
1229
|
-
"Password is set — type to replace, leave empty + Save to clear (مترجم)",
|
|
1230
|
+
passwordSetPlaceholder: "Password is set — type to replace (مترجم)",
|
|
1230
1231
|
noPasswordPlaceholder: "No password (مترجم)",
|
|
1232
|
+
passwordWhitespaceOnly: "Spaces alone aren't a valid password. (مترجم)",
|
|
1233
|
+
removePassword: "Remove (مترجم)",
|
|
1231
1234
|
expiry: "Expiry (مترجم)",
|
|
1232
1235
|
viewerOptions: "Viewer options (مترجم)",
|
|
1233
1236
|
comments: "Comments (مترجم)",
|
|
@@ -468,6 +468,8 @@ const messages = {
|
|
|
468
468
|
retryAgentLink: "Erneut versuchen",
|
|
469
469
|
gifPreview: "GIF-Vorschau",
|
|
470
470
|
openPlayer: "Spieler öffnen",
|
|
471
|
+
chooseFile: "Datei auswählen",
|
|
472
|
+
remove: "Entfernen",
|
|
471
473
|
downloadMp4: "Laden Sie MP4 herunter",
|
|
472
474
|
embedsNeedPublic: "Für Einbettungen ist ein öffentlicher Clip erforderlich",
|
|
473
475
|
embedPublicDescription:
|
|
@@ -1251,9 +1253,11 @@ Alle sichtbaren Änderungen für Clips-Nutzer werden hier dokumentiert. Du kanns
|
|
|
1251
1253
|
visibilityOrg: "Organization (Lokalisiert)",
|
|
1252
1254
|
visibilityPublic: "Public (Lokalisiert)",
|
|
1253
1255
|
passwordProtection: "Password protection (Lokalisiert)",
|
|
1254
|
-
passwordSetPlaceholder:
|
|
1255
|
-
"Password is set — type to replace, leave empty + Save to clear (Lokalisiert)",
|
|
1256
|
+
passwordSetPlaceholder: "Password is set — type to replace (Lokalisiert)",
|
|
1256
1257
|
noPasswordPlaceholder: "No password (Lokalisiert)",
|
|
1258
|
+
passwordWhitespaceOnly:
|
|
1259
|
+
"Spaces alone aren't a valid password. (Lokalisiert)",
|
|
1260
|
+
removePassword: "Remove (Lokalisiert)",
|
|
1257
1261
|
expiry: "Expiry (Lokalisiert)",
|
|
1258
1262
|
viewerOptions: "Viewer options (Lokalisiert)",
|
|
1259
1263
|
comments: "Comments (Lokalisiert)",
|