@agent-native/core 0.102.2 → 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 +1 -1
- package/corpus/core/CHANGELOG.md +6 -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/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/clips/app/components/editor/editor-layout.tsx +12 -2
- package/corpus/templates/clips/app/components/player/settings-panel.tsx +30 -12
- package/corpus/templates/clips/app/components/player/video-player.tsx +5 -1
- package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +22 -3
- package/corpus/templates/clips/app/i18n/ar-SA.ts +3 -2
- package/corpus/templates/clips/app/i18n/de-DE.ts +4 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +3 -2
- package/corpus/templates/clips/app/i18n/es-ES.ts +4 -2
- package/corpus/templates/clips/app/i18n/fr-FR.ts +3 -2
- package/corpus/templates/clips/app/i18n/hi-IN.ts +3 -2
- package/corpus/templates/clips/app/i18n/ja-JP.ts +4 -1
- package/corpus/templates/clips/app/i18n/ko-KR.ts +3 -2
- package/corpus/templates/clips/app/i18n/pt-BR.ts +4 -2
- package/corpus/templates/clips/app/i18n/zh-CN.ts +3 -2
- package/corpus/templates/clips/app/i18n/zh-TW.ts +3 -2
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +27 -1
- 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/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-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/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/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/observability/routes.d.ts +5 -5
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- 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
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sendToAgentChat,
|
|
3
|
+
useActionMutation,
|
|
4
|
+
useActionQuery,
|
|
5
|
+
useT,
|
|
6
|
+
} from "@agent-native/core/client";
|
|
7
|
+
import {
|
|
8
|
+
useSetHeaderActions,
|
|
9
|
+
useSetPageTitle,
|
|
10
|
+
} from "@agent-native/toolkit/app-shell";
|
|
11
|
+
import {
|
|
12
|
+
IconArrowLeft,
|
|
13
|
+
IconBulb,
|
|
14
|
+
IconChevronDown,
|
|
15
|
+
IconChevronRight,
|
|
16
|
+
IconListCheck,
|
|
17
|
+
IconPhoto,
|
|
18
|
+
IconTextCaption,
|
|
19
|
+
} from "@tabler/icons-react";
|
|
20
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
21
|
+
import {
|
|
22
|
+
useBeforeUnload,
|
|
23
|
+
useBlocker,
|
|
24
|
+
useNavigate,
|
|
25
|
+
useParams,
|
|
26
|
+
} from "react-router";
|
|
27
|
+
import { toast } from "sonner";
|
|
28
|
+
|
|
29
|
+
import { GenerationPresetsPanel } from "@/components/library/GenerationPresetsPanel";
|
|
30
|
+
import { Badge } from "@/components/ui/badge";
|
|
31
|
+
import { Button } from "@/components/ui/button";
|
|
32
|
+
import {
|
|
33
|
+
Dialog,
|
|
34
|
+
DialogContent,
|
|
35
|
+
DialogDescription,
|
|
36
|
+
DialogFooter,
|
|
37
|
+
DialogHeader,
|
|
38
|
+
DialogTitle,
|
|
39
|
+
} from "@/components/ui/dialog";
|
|
40
|
+
import { Input } from "@/components/ui/input";
|
|
41
|
+
import { Label } from "@/components/ui/label";
|
|
42
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
43
|
+
import { getLibraryCustomInstructions } from "@/lib/libraries";
|
|
44
|
+
|
|
45
|
+
function paletteDraftFromColors(colors: unknown): string {
|
|
46
|
+
return Array.isArray(colors)
|
|
47
|
+
? colors
|
|
48
|
+
.filter((color) => typeof color === "string")
|
|
49
|
+
.map((color) => color.toLowerCase())
|
|
50
|
+
.join(", ")
|
|
51
|
+
: "";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function parsePaletteDraft(value: string): string[] {
|
|
55
|
+
const seen = new Set<string>();
|
|
56
|
+
const colors: string[] = [];
|
|
57
|
+
for (const raw of value.split(/[\s,]+/)) {
|
|
58
|
+
const trimmed = raw.trim();
|
|
59
|
+
if (!trimmed) continue;
|
|
60
|
+
const color = trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
|
|
61
|
+
if (!/^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test(color)) continue;
|
|
62
|
+
const normalized = color.toLowerCase();
|
|
63
|
+
if (seen.has(normalized)) continue;
|
|
64
|
+
seen.add(normalized);
|
|
65
|
+
colors.push(normalized);
|
|
66
|
+
}
|
|
67
|
+
return colors;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default function BrandKitSettingsRoute() {
|
|
71
|
+
const t = useT();
|
|
72
|
+
const navigate = useNavigate();
|
|
73
|
+
const { id } = useParams();
|
|
74
|
+
const libraryId = id ?? "";
|
|
75
|
+
const { data } = useActionQuery("get-library", { id: libraryId }) as any;
|
|
76
|
+
const { data: presetData } = useActionQuery("list-generation-presets", {
|
|
77
|
+
libraryId,
|
|
78
|
+
}) as any;
|
|
79
|
+
const updateLibrary = useActionMutation("update-library");
|
|
80
|
+
|
|
81
|
+
const library = data?.library;
|
|
82
|
+
const assets = (data?.assets ?? []) as any[];
|
|
83
|
+
const generationPresets = ((presetData as any)?.presets ?? []) as any[];
|
|
84
|
+
|
|
85
|
+
const [titleDraft, setTitleDraft] = useState("");
|
|
86
|
+
const [descriptionDraft, setDescriptionDraft] = useState("");
|
|
87
|
+
const [styleDescriptionDraft, setStyleDescriptionDraft] = useState("");
|
|
88
|
+
const [customInstructionsDraft, setCustomInstructionsDraft] = useState("");
|
|
89
|
+
const [paletteDraft, setPaletteDraft] = useState("");
|
|
90
|
+
const [detailsOpen, setDetailsOpen] = useState(false);
|
|
91
|
+
const [initializedLibraryId, setInitializedLibraryId] = useState<
|
|
92
|
+
string | null
|
|
93
|
+
>(null);
|
|
94
|
+
|
|
95
|
+
const isDirty = useMemo(() => {
|
|
96
|
+
if (!library) return false;
|
|
97
|
+
if (titleDraft.trim() !== (library.title ?? "").trim()) return true;
|
|
98
|
+
if (descriptionDraft.trim() !== (library.description ?? "").trim())
|
|
99
|
+
return true;
|
|
100
|
+
if (styleDescriptionDraft !== (library.styleBrief?.description ?? ""))
|
|
101
|
+
return true;
|
|
102
|
+
if (
|
|
103
|
+
customInstructionsDraft.trim() !==
|
|
104
|
+
(getLibraryCustomInstructions(library) ?? "")
|
|
105
|
+
)
|
|
106
|
+
return true;
|
|
107
|
+
if (
|
|
108
|
+
parsePaletteDraft(paletteDraft).join(", ") !==
|
|
109
|
+
paletteDraftFromColors(library.styleBrief?.palette)
|
|
110
|
+
)
|
|
111
|
+
return true;
|
|
112
|
+
return false;
|
|
113
|
+
}, [
|
|
114
|
+
library,
|
|
115
|
+
titleDraft,
|
|
116
|
+
descriptionDraft,
|
|
117
|
+
styleDescriptionDraft,
|
|
118
|
+
customInstructionsDraft,
|
|
119
|
+
paletteDraft,
|
|
120
|
+
]);
|
|
121
|
+
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (!library) return;
|
|
124
|
+
if (initializedLibraryId === library.id && isDirty) return;
|
|
125
|
+
setTitleDraft(library.title ?? "");
|
|
126
|
+
setDescriptionDraft(library.description ?? "");
|
|
127
|
+
setStyleDescriptionDraft(library.styleBrief?.description ?? "");
|
|
128
|
+
setCustomInstructionsDraft(getLibraryCustomInstructions(library) ?? "");
|
|
129
|
+
setPaletteDraft(paletteDraftFromColors(library.styleBrief?.palette));
|
|
130
|
+
if (initializedLibraryId !== library.id) {
|
|
131
|
+
const isNewLibrary =
|
|
132
|
+
!library.description &&
|
|
133
|
+
!getLibraryCustomInstructions(library) &&
|
|
134
|
+
!library.styleBrief?.description &&
|
|
135
|
+
!(library.styleBrief?.palette ?? []).length;
|
|
136
|
+
setDetailsOpen(isNewLibrary);
|
|
137
|
+
setInitializedLibraryId(library.id);
|
|
138
|
+
}
|
|
139
|
+
}, [library, initializedLibraryId, isDirty]);
|
|
140
|
+
|
|
141
|
+
const isTitleValid = titleDraft.trim().length > 0;
|
|
142
|
+
|
|
143
|
+
function saveAll() {
|
|
144
|
+
if (!library || !isDirty || !isTitleValid) return;
|
|
145
|
+
const trimmedTitle = titleDraft.trim();
|
|
146
|
+
const palette = parsePaletteDraft(paletteDraft);
|
|
147
|
+
updateLibrary.mutate(
|
|
148
|
+
{
|
|
149
|
+
id: library.id,
|
|
150
|
+
title: trimmedTitle,
|
|
151
|
+
description: descriptionDraft.trim() || null,
|
|
152
|
+
customInstructions: customInstructionsDraft.trim(),
|
|
153
|
+
styleBrief: {
|
|
154
|
+
...library.styleBrief,
|
|
155
|
+
description: styleDescriptionDraft,
|
|
156
|
+
palette,
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
onSuccess: () => {
|
|
161
|
+
setPaletteDraft(palette.join(", "));
|
|
162
|
+
toast.success(t("brandKits.updated"));
|
|
163
|
+
},
|
|
164
|
+
onError: (error: Error) =>
|
|
165
|
+
toast.error(error.message || t("brandKits.updateFailed")),
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const navigationBlocker = useBlocker(
|
|
171
|
+
useCallback(
|
|
172
|
+
({ currentLocation, nextLocation }) =>
|
|
173
|
+
isDirty && currentLocation.pathname !== nextLocation.pathname,
|
|
174
|
+
[isDirty],
|
|
175
|
+
),
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
useBeforeUnload(
|
|
179
|
+
useCallback(
|
|
180
|
+
(event: BeforeUnloadEvent) => {
|
|
181
|
+
if (!isDirty) return;
|
|
182
|
+
event.preventDefault();
|
|
183
|
+
event.returnValue = "";
|
|
184
|
+
},
|
|
185
|
+
[isDirty],
|
|
186
|
+
),
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
function handleBack() {
|
|
190
|
+
navigate(`/library/${libraryId}`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function keepEditing() {
|
|
194
|
+
if (navigationBlocker.state === "blocked") navigationBlocker.reset();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function discardAndLeave() {
|
|
198
|
+
if (navigationBlocker.state === "blocked") navigationBlocker.proceed();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function analyzeBrand() {
|
|
202
|
+
if (!library) return;
|
|
203
|
+
const referenceCount = assets.filter(
|
|
204
|
+
(asset) => asset.status === "reference",
|
|
205
|
+
).length;
|
|
206
|
+
sendToAgentChat({
|
|
207
|
+
message: [
|
|
208
|
+
"Analyze this Assets library brand.",
|
|
209
|
+
`Call analyze-collection-style with libraryId: ${library.id}.`,
|
|
210
|
+
"Update the reusable style brief with palette and visual traits, then summarize what changed.",
|
|
211
|
+
].join("\n"),
|
|
212
|
+
context: [
|
|
213
|
+
"## Assets library context",
|
|
214
|
+
`Library: ${library.title} (${library.id})`,
|
|
215
|
+
`Description: ${library.description || ""}`,
|
|
216
|
+
`Reference assets: ${referenceCount}`,
|
|
217
|
+
`Current style brief: ${JSON.stringify(library.styleBrief ?? {})}`,
|
|
218
|
+
getLibraryCustomInstructions(library)
|
|
219
|
+
? `Custom instructions: ${getLibraryCustomInstructions(library)}`
|
|
220
|
+
: "Custom instructions: none",
|
|
221
|
+
].join("\n"),
|
|
222
|
+
submit: true,
|
|
223
|
+
newTab: true,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
useSetPageTitle(
|
|
228
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
229
|
+
<Button
|
|
230
|
+
variant="ghost"
|
|
231
|
+
size="icon"
|
|
232
|
+
className="-ms-2 h-7 w-7 shrink-0"
|
|
233
|
+
onClick={handleBack}
|
|
234
|
+
aria-label={t("brandKitDetail.backToLibrary")}
|
|
235
|
+
>
|
|
236
|
+
<IconArrowLeft className="h-4 w-4" />
|
|
237
|
+
</Button>
|
|
238
|
+
<h1 className="truncate text-lg font-semibold tracking-tight">
|
|
239
|
+
{t("brandKitDetail.settingsTitle")}
|
|
240
|
+
</h1>
|
|
241
|
+
{library ? <Badge variant="outline">{library.title}</Badge> : null}
|
|
242
|
+
</div>,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
useSetHeaderActions(
|
|
246
|
+
library ? (
|
|
247
|
+
<Button
|
|
248
|
+
size="sm"
|
|
249
|
+
onClick={saveAll}
|
|
250
|
+
disabled={!isDirty || !isTitleValid || updateLibrary.isPending}
|
|
251
|
+
>
|
|
252
|
+
{updateLibrary.isPending
|
|
253
|
+
? t("brandKitDetail.saving")
|
|
254
|
+
: t("brandKitDetail.save")}
|
|
255
|
+
</Button>
|
|
256
|
+
) : null,
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
if (!library) {
|
|
260
|
+
return (
|
|
261
|
+
<div className="p-6 text-sm text-muted-foreground">
|
|
262
|
+
{t("library.loadingBrandKit")}
|
|
263
|
+
</div>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<div className="mx-auto flex w-full max-w-5xl flex-col gap-6 px-6 py-6">
|
|
269
|
+
<div className="flex flex-col gap-4">
|
|
270
|
+
<div className="space-y-4 rounded-lg border border-border p-4">
|
|
271
|
+
<Label htmlFor="brand-kit-title">{t("brandKitDetail.name")}</Label>
|
|
272
|
+
<Input
|
|
273
|
+
id="brand-kit-title"
|
|
274
|
+
value={titleDraft}
|
|
275
|
+
onChange={(event) => setTitleDraft(event.target.value)}
|
|
276
|
+
placeholder={t("brandKits.namePlaceholder")}
|
|
277
|
+
/>
|
|
278
|
+
<Label htmlFor="brand-kit-description">
|
|
279
|
+
{t("assetDetail.description")}
|
|
280
|
+
</Label>
|
|
281
|
+
<Textarea
|
|
282
|
+
id="brand-kit-description"
|
|
283
|
+
value={descriptionDraft}
|
|
284
|
+
onChange={(event) => setDescriptionDraft(event.target.value)}
|
|
285
|
+
placeholder={t("brandKits.editDescriptionPlaceholder")}
|
|
286
|
+
/>
|
|
287
|
+
<div>
|
|
288
|
+
<h3 className="text-sm font-semibold">
|
|
289
|
+
{t("brandKitDetail.agentUsage")}
|
|
290
|
+
</h3>
|
|
291
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
292
|
+
{t("brandKitDetail.agentUsageDescription")}
|
|
293
|
+
</p>
|
|
294
|
+
<code className="mt-3 block rounded-md bg-muted p-3 text-xs">
|
|
295
|
+
{library.id}
|
|
296
|
+
</code>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
|
|
300
|
+
<div className="rounded-lg border border-border p-4">
|
|
301
|
+
<button
|
|
302
|
+
type="button"
|
|
303
|
+
className="flex w-full items-center justify-between gap-2 text-left"
|
|
304
|
+
onClick={() => setDetailsOpen((open) => !open)}
|
|
305
|
+
aria-expanded={detailsOpen}
|
|
306
|
+
>
|
|
307
|
+
<div>
|
|
308
|
+
<h3 className="text-sm font-semibold">
|
|
309
|
+
{t("brandKitDetail.setupGuide")}
|
|
310
|
+
</h3>
|
|
311
|
+
<p className="mt-1 text-sm text-muted-foreground">
|
|
312
|
+
{t("brandKitDetail.setupGuideDescription")}
|
|
313
|
+
</p>
|
|
314
|
+
</div>
|
|
315
|
+
{detailsOpen ? (
|
|
316
|
+
<IconChevronDown className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
317
|
+
) : (
|
|
318
|
+
<IconChevronRight className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
319
|
+
)}
|
|
320
|
+
</button>
|
|
321
|
+
{detailsOpen ? (
|
|
322
|
+
<ul className="mt-4 space-y-3">
|
|
323
|
+
<li className="flex gap-3">
|
|
324
|
+
<IconPhoto className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
325
|
+
<div>
|
|
326
|
+
<div className="text-sm font-medium">
|
|
327
|
+
{t("brandKitDetail.setupGuideReferences")}
|
|
328
|
+
</div>
|
|
329
|
+
<p className="text-sm text-muted-foreground">
|
|
330
|
+
{t("brandKitDetail.setupGuideReferencesHint")}
|
|
331
|
+
</p>
|
|
332
|
+
</div>
|
|
333
|
+
</li>
|
|
334
|
+
<li className="flex gap-3">
|
|
335
|
+
<IconTextCaption className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
336
|
+
<div>
|
|
337
|
+
<div className="text-sm font-medium">
|
|
338
|
+
{t("brandKitDetail.setupGuideStyleDescription")}
|
|
339
|
+
</div>
|
|
340
|
+
<p className="text-sm text-muted-foreground">
|
|
341
|
+
{t("brandKitDetail.setupGuideStyleDescriptionHint")}
|
|
342
|
+
</p>
|
|
343
|
+
</div>
|
|
344
|
+
</li>
|
|
345
|
+
<li className="flex gap-3">
|
|
346
|
+
<IconListCheck className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
347
|
+
<div>
|
|
348
|
+
<div className="text-sm font-medium">
|
|
349
|
+
{t("brandKitDetail.setupGuideInstructions")}
|
|
350
|
+
</div>
|
|
351
|
+
<p className="text-sm text-muted-foreground">
|
|
352
|
+
{t("brandKitDetail.setupGuideInstructionsHint")}
|
|
353
|
+
</p>
|
|
354
|
+
</div>
|
|
355
|
+
</li>
|
|
356
|
+
<li className="flex gap-3">
|
|
357
|
+
<IconBulb className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
358
|
+
<div>
|
|
359
|
+
<div className="text-sm font-medium">
|
|
360
|
+
{t("brandKitDetail.setupGuidePresets")}
|
|
361
|
+
</div>
|
|
362
|
+
<p className="text-sm text-muted-foreground">
|
|
363
|
+
{t("brandKitDetail.setupGuidePresetsHint")}
|
|
364
|
+
</p>
|
|
365
|
+
</div>
|
|
366
|
+
</li>
|
|
367
|
+
</ul>
|
|
368
|
+
) : null}
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
|
|
372
|
+
<div className="space-y-4 rounded-lg border border-border p-4">
|
|
373
|
+
<Label>{t("brandKitDetail.styleDescription")}</Label>
|
|
374
|
+
<Textarea
|
|
375
|
+
value={styleDescriptionDraft}
|
|
376
|
+
onChange={(event) => setStyleDescriptionDraft(event.target.value)}
|
|
377
|
+
className="min-h-40"
|
|
378
|
+
/>
|
|
379
|
+
<Label>{t("brandKitDetail.customInstructions")}</Label>
|
|
380
|
+
<Textarea
|
|
381
|
+
value={customInstructionsDraft}
|
|
382
|
+
onChange={(event) => setCustomInstructionsDraft(event.target.value)}
|
|
383
|
+
placeholder={t("brandKitDetail.customInstructionsPlaceholder")}
|
|
384
|
+
className="min-h-28"
|
|
385
|
+
/>
|
|
386
|
+
<div className="flex items-center justify-between gap-3">
|
|
387
|
+
<div>
|
|
388
|
+
<div className="text-sm font-medium">
|
|
389
|
+
{t("brandKitDetail.palette")}
|
|
390
|
+
</div>
|
|
391
|
+
<div className="mt-2 flex flex-wrap gap-2">
|
|
392
|
+
{(library.styleBrief?.palette ?? []).map((color: string) => (
|
|
393
|
+
<span
|
|
394
|
+
key={color}
|
|
395
|
+
className="h-7 w-7 rounded-md border border-border"
|
|
396
|
+
style={{ backgroundColor: color }}
|
|
397
|
+
title={color}
|
|
398
|
+
/>
|
|
399
|
+
))}
|
|
400
|
+
</div>
|
|
401
|
+
<Input
|
|
402
|
+
value={paletteDraft}
|
|
403
|
+
onChange={(event) => setPaletteDraft(event.target.value)}
|
|
404
|
+
placeholder={"#111827, #f8fafc, #2563eb"}
|
|
405
|
+
className="mt-3 h-9 max-w-md text-xs"
|
|
406
|
+
/>
|
|
407
|
+
</div>
|
|
408
|
+
<Button variant="outline" onClick={analyzeBrand}>
|
|
409
|
+
{library.settings?.brandAnalysis?.analyzedAt
|
|
410
|
+
? t("brandKitDetail.refreshBrand")
|
|
411
|
+
: t("brandKitDetail.analyzeBrand")}
|
|
412
|
+
</Button>
|
|
413
|
+
</div>
|
|
414
|
+
</div>
|
|
415
|
+
|
|
416
|
+
<GenerationPresetsPanel
|
|
417
|
+
libraryId={libraryId}
|
|
418
|
+
presets={generationPresets}
|
|
419
|
+
/>
|
|
420
|
+
|
|
421
|
+
<Dialog
|
|
422
|
+
open={navigationBlocker.state === "blocked"}
|
|
423
|
+
onOpenChange={(open) => {
|
|
424
|
+
if (!open) keepEditing();
|
|
425
|
+
}}
|
|
426
|
+
>
|
|
427
|
+
<DialogContent>
|
|
428
|
+
<DialogHeader>
|
|
429
|
+
<DialogTitle>{t("brandKitDetail.unsavedChangesTitle")}</DialogTitle>
|
|
430
|
+
<DialogDescription>
|
|
431
|
+
{t("brandKitDetail.unsavedChangesDescription")}
|
|
432
|
+
</DialogDescription>
|
|
433
|
+
</DialogHeader>
|
|
434
|
+
<DialogFooter>
|
|
435
|
+
<Button variant="outline" onClick={keepEditing}>
|
|
436
|
+
{t("brandKitDetail.keepEditing")}
|
|
437
|
+
</Button>
|
|
438
|
+
<Button variant="destructive" onClick={discardAndLeave}>
|
|
439
|
+
{t("brandKitDetail.discardChanges")}
|
|
440
|
+
</Button>
|
|
441
|
+
</DialogFooter>
|
|
442
|
+
</DialogContent>
|
|
443
|
+
</Dialog>
|
|
444
|
+
</div>
|
|
445
|
+
);
|
|
446
|
+
}
|
|
@@ -415,13 +415,20 @@ export function EditorLayout({ recordingId, className }: EditorLayoutProps) {
|
|
|
415
415
|
if (!recording?.id) return;
|
|
416
416
|
const next = readPlaybackSpeedPreference(defaultPreviewSpeed);
|
|
417
417
|
setPlaybackSpeed(next);
|
|
418
|
-
if (videoRef.current)
|
|
418
|
+
if (videoRef.current) {
|
|
419
|
+
videoRef.current.defaultPlaybackRate = next;
|
|
420
|
+
videoRef.current.playbackRate = next;
|
|
421
|
+
}
|
|
419
422
|
}, [defaultPreviewSpeed, recording?.id]);
|
|
420
423
|
|
|
421
424
|
// Keep the editor preview speed visible and in sync with the media element.
|
|
425
|
+
// `defaultPlaybackRate` is set too so a `videoUrl` source swap that resets
|
|
426
|
+
// `playbackRate` (some browsers do this on load) falls back to the chosen
|
|
427
|
+
// speed instead of 1x.
|
|
422
428
|
useEffect(() => {
|
|
423
429
|
const v = videoRef.current;
|
|
424
430
|
if (!v) return;
|
|
431
|
+
v.defaultPlaybackRate = playbackSpeed;
|
|
425
432
|
v.playbackRate = playbackSpeed;
|
|
426
433
|
}, [playbackSpeed, videoUrl]);
|
|
427
434
|
|
|
@@ -429,7 +436,10 @@ export function EditorLayout({ recordingId, className }: EditorLayoutProps) {
|
|
|
429
436
|
const next = parsePlaybackSpeed(rate) ?? 1.2;
|
|
430
437
|
setPlaybackSpeed(next);
|
|
431
438
|
savePlaybackSpeedPreference(next);
|
|
432
|
-
if (videoRef.current)
|
|
439
|
+
if (videoRef.current) {
|
|
440
|
+
videoRef.current.defaultPlaybackRate = next;
|
|
441
|
+
videoRef.current.playbackRate = next;
|
|
442
|
+
}
|
|
433
443
|
}, []);
|
|
434
444
|
|
|
435
445
|
// Keep the playheadMs in sync with the element's currentTime.
|
|
@@ -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
|
|
|
@@ -679,7 +679,10 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
679
679
|
(isPlaying || playAttemptPendingRef.current || !v.paused),
|
|
680
680
|
);
|
|
681
681
|
|
|
682
|
-
if (v)
|
|
682
|
+
if (v) {
|
|
683
|
+
v.defaultPlaybackRate = nextSpeed;
|
|
684
|
+
v.playbackRate = nextSpeed;
|
|
685
|
+
}
|
|
683
686
|
setSpeed(nextSpeed);
|
|
684
687
|
savePlaybackSpeedPreference(nextSpeed);
|
|
685
688
|
onSpeedChange?.(nextSpeed);
|
|
@@ -778,6 +781,7 @@ export const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
|
|
|
778
781
|
const v = videoRef.current;
|
|
779
782
|
if (!v) return;
|
|
780
783
|
const initialSpeed = readPlaybackSpeedPreference(defaultSpeed);
|
|
784
|
+
v.defaultPlaybackRate = initialSpeed;
|
|
781
785
|
v.playbackRate = initialSpeed;
|
|
782
786
|
setSpeed(initialSpeed);
|
|
783
787
|
onSpeedChange?.(initialSpeed);
|
|
@@ -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 });
|
|
@@ -1227,9 +1227,10 @@ const messages = {
|
|
|
1227
1227
|
visibilityOrg: "Organization (مترجم)",
|
|
1228
1228
|
visibilityPublic: "Public (مترجم)",
|
|
1229
1229
|
passwordProtection: "Password protection (مترجم)",
|
|
1230
|
-
passwordSetPlaceholder:
|
|
1231
|
-
"Password is set — type to replace, leave empty + Save to clear (مترجم)",
|
|
1230
|
+
passwordSetPlaceholder: "Password is set — type to replace (مترجم)",
|
|
1232
1231
|
noPasswordPlaceholder: "No password (مترجم)",
|
|
1232
|
+
passwordWhitespaceOnly: "Spaces alone aren't a valid password. (مترجم)",
|
|
1233
|
+
removePassword: "Remove (مترجم)",
|
|
1233
1234
|
expiry: "Expiry (مترجم)",
|
|
1234
1235
|
viewerOptions: "Viewer options (مترجم)",
|
|
1235
1236
|
comments: "Comments (مترجم)",
|
|
@@ -1253,9 +1253,11 @@ Alle sichtbaren Änderungen für Clips-Nutzer werden hier dokumentiert. Du kanns
|
|
|
1253
1253
|
visibilityOrg: "Organization (Lokalisiert)",
|
|
1254
1254
|
visibilityPublic: "Public (Lokalisiert)",
|
|
1255
1255
|
passwordProtection: "Password protection (Lokalisiert)",
|
|
1256
|
-
passwordSetPlaceholder:
|
|
1257
|
-
"Password is set — type to replace, leave empty + Save to clear (Lokalisiert)",
|
|
1256
|
+
passwordSetPlaceholder: "Password is set — type to replace (Lokalisiert)",
|
|
1258
1257
|
noPasswordPlaceholder: "No password (Lokalisiert)",
|
|
1258
|
+
passwordWhitespaceOnly:
|
|
1259
|
+
"Spaces alone aren't a valid password. (Lokalisiert)",
|
|
1260
|
+
removePassword: "Remove (Lokalisiert)",
|
|
1259
1261
|
expiry: "Expiry (Lokalisiert)",
|
|
1260
1262
|
viewerOptions: "Viewer options (Lokalisiert)",
|
|
1261
1263
|
comments: "Comments (Lokalisiert)",
|
|
@@ -1221,9 +1221,10 @@ All notable user-facing changes to Clips are documented here. Open it any time f
|
|
|
1221
1221
|
visibilityOrg: "Organization",
|
|
1222
1222
|
visibilityPublic: "Public",
|
|
1223
1223
|
passwordProtection: "Password protection",
|
|
1224
|
-
passwordSetPlaceholder:
|
|
1225
|
-
"Password is set — type to replace, leave empty + Save to clear",
|
|
1224
|
+
passwordSetPlaceholder: "Password is set — type to replace",
|
|
1226
1225
|
noPasswordPlaceholder: "No password",
|
|
1226
|
+
passwordWhitespaceOnly: "Spaces alone aren't a valid password.",
|
|
1227
|
+
removePassword: "Remove",
|
|
1227
1228
|
expiry: "Expiry",
|
|
1228
1229
|
viewerOptions: "Viewer options",
|
|
1229
1230
|
comments: "Comments",
|
|
@@ -1246,9 +1246,11 @@ Todos los cambios visibles para los usuarios de Clips se documentan aquí. Puede
|
|
|
1246
1246
|
visibilityOrg: "Organization (Localizado)",
|
|
1247
1247
|
visibilityPublic: "Public (Localizado)",
|
|
1248
1248
|
passwordProtection: "Password protection (Localizado)",
|
|
1249
|
-
passwordSetPlaceholder:
|
|
1250
|
-
"Password is set — type to replace, leave empty + Save to clear (Localizado)",
|
|
1249
|
+
passwordSetPlaceholder: "Password is set — type to replace (Localizado)",
|
|
1251
1250
|
noPasswordPlaceholder: "No password (Localizado)",
|
|
1251
|
+
passwordWhitespaceOnly:
|
|
1252
|
+
"Spaces alone aren't a valid password. (Localizado)",
|
|
1253
|
+
removePassword: "Remove (Localizado)",
|
|
1252
1254
|
expiry: "Expiry (Localizado)",
|
|
1253
1255
|
viewerOptions: "Viewer options (Localizado)",
|
|
1254
1256
|
comments: "Comments (Localizado)",
|
|
@@ -1248,9 +1248,10 @@ Tous les changements visibles par les utilisateurs de Clips sont documentés ici
|
|
|
1248
1248
|
visibilityOrg: "Organization (Localisé)",
|
|
1249
1249
|
visibilityPublic: "Public (Localisé)",
|
|
1250
1250
|
passwordProtection: "Password protection (Localisé)",
|
|
1251
|
-
passwordSetPlaceholder:
|
|
1252
|
-
"Password is set — type to replace, leave empty + Save to clear (Localisé)",
|
|
1251
|
+
passwordSetPlaceholder: "Password is set — type to replace (Localisé)",
|
|
1253
1252
|
noPasswordPlaceholder: "No password (Localisé)",
|
|
1253
|
+
passwordWhitespaceOnly: "Spaces alone aren't a valid password. (Localisé)",
|
|
1254
|
+
removePassword: "Remove (Localisé)",
|
|
1254
1255
|
expiry: "Expiry (Localisé)",
|
|
1255
1256
|
viewerOptions: "Viewer options (Localisé)",
|
|
1256
1257
|
comments: "Comments (Localisé)",
|