@burdenoff/microfe-vibecontrols 2026.709.2 → 2026.710.1
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/components/agent-manager/AgentManagerBookmarksDrawer.js +87 -85
- package/dist/components/agent-manager/AgentManagerBookmarksDrawer.js.map +1 -1
- package/dist/components/ai/InstructionFilesManager.js +138 -136
- package/dist/components/ai/InstructionFilesManager.js.map +1 -1
- package/dist/components/ai/PluginHarnessPicker.js +70 -64
- package/dist/components/ai/PluginHarnessPicker.js.map +1 -1
- package/dist/components/docs/DocsEditPanel.js +1 -1
- package/dist/components/docs/DocsEditPanel.js.map +1 -1
- package/dist/components/docs/DocsPageTree.js +1 -1
- package/dist/components/docs/DocsPageTree.js.map +1 -1
- package/dist/components/docs/DocsTabPanel.js +108 -106
- package/dist/components/docs/DocsTabPanel.js.map +1 -1
- package/dist/components/notes/NotesTabPanel.js +99 -99
- package/dist/components/notes/NotesTabPanel.js.map +1 -1
- package/dist/components/scorecard/ScorecardPanel.js +309 -308
- package/dist/components/scorecard/ScorecardPanel.js.map +1 -1
- package/dist/components/sessions/SessionPublicLinkPanel.js +104 -102
- package/dist/components/sessions/SessionPublicLinkPanel.js.map +1 -1
- package/dist/components/settings/AdvancedSection.js +92 -90
- package/dist/components/settings/AdvancedSection.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js +104 -102
- package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js.map +1 -1
- package/dist/constants/permissions.js +7 -0
- package/dist/constants/permissions.js.map +1 -1
- package/dist/pages/ActionsPage.js +303 -303
- package/dist/pages/ActionsPage.js.map +1 -1
- package/dist/pages/AgentDetailsPage.js +520 -516
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/AgentsPage.js +287 -283
- package/dist/pages/AgentsPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +554 -549
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/VibeAuditPage.js +2 -2
- package/dist/pages/VibeAuditPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js +257 -257
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/dist/pages/VibeDetailsPage.js +688 -693
- package/dist/pages/VibeDetailsPage.js.map +1 -1
- package/dist/pages/VibesPage.js +49 -41
- package/dist/pages/VibesPage.js.map +1 -1
- package/dist/pages/WebhooksPage.js +162 -138
- package/dist/pages/WebhooksPage.js.map +1 -1
- package/dist/pages/tunnels/DomainsTab.js +54 -47
- package/dist/pages/tunnels/DomainsTab.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionPublicLinkPanel.js","names":[],"sources":["../../../src/components/sessions/SessionPublicLinkPanel.tsx"],"sourcesContent":["import type { FC, FormEvent, ReactNode } from 'react';\nimport { useState } from 'react';\nimport { Link2, Keyboard, Eye, Lock, Copy, Trash2, Plus, X, Check } from 'lucide-react';\n\nimport { LoadingState, ErrorState } from '@/components/shared';\nimport { notify } from '@/lib/toast';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n useGetSessionShareLinksQuery,\n useCreateSessionShareLinkMutation,\n useRevokeSessionShareLinkMutation,\n type SessionShareLinkFieldsFragment,\n} from '@/generated/wspace-operations';\nimport { SessionSharePermission } from '@/generated/wspace-types';\n\ninterface SessionPublicLinkPanelProps {\n sessionId: string;\n sessionName: string;\n onClose: () => void;\n}\n\nconst EXPIRY_OPTIONS = [\n { label: '1 hour', hours: 1 },\n { label: '4 hours', hours: 4 },\n { label: '24 hours', hours: 24 },\n { label: 'No expiry', hours: 0 },\n];\n\n/**\n * SessionPublicLinkPanel — create and manage expiring share links to a live terminal session, with\n * the controls the live-session-share story promises but the generic member-share dialog dropped:\n * viewer vs interactive permission, an **expiry**, an optional **password**, and an optional\n * **usage limit**. Links point at the live `share/:token` page (the real collaborative terminal,\n * not an auth'd accept page). Backed by the existing `createSessionShareLink` mutation — no backend\n * change.\n */\nexport const SessionPublicLinkPanel: FC<SessionPublicLinkPanelProps> = ({\n sessionId,\n sessionName,\n onClose,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const { data, loading, error, refetch } = useGetSessionShareLinksQuery({\n variables: { sessionId },\n fetchPolicy: 'cache-and-network',\n });\n const [createLink, { loading: creating }] = useCreateSessionShareLinkMutation();\n const [revokeLink] = useRevokeSessionShareLinkMutation();\n\n const [interactive, setInteractive] = useState(false);\n const [expiryHours, setExpiryHours] = useState(4);\n const [password, setPassword] = useState('');\n const [maxUses, setMaxUses] = useState('');\n const [copiedId, setCopiedId] = useState<string | null>(null);\n\n const links: SessionShareLinkFieldsFragment[] = data?.sessionShareLinks ?? [];\n const activeLinks = links.filter((l) => l.status === 'ACTIVE');\n\n const copy = async (url: string, id: string): Promise<void> => {\n try {\n await navigator.clipboard.writeText(url);\n setCopiedId(id);\n notify.success(tr('sessLink.copied', 'Link copied'));\n window.setTimeout(() => setCopiedId(null), 1500);\n } catch {\n notify.error(tr('sessLink.copyFailed', 'Could not copy — copy it manually'), url);\n }\n };\n\n const handleCreate = async (e: FormEvent): Promise<void> => {\n e.preventDefault();\n const parsedMax = maxUses.trim() ? Number.parseInt(maxUses.trim(), 10) : null;\n if (parsedMax !== null && (!Number.isFinite(parsedMax) || parsedMax < 1)) {\n notify.error(tr('sessLink.badMax', 'Max uses must be a positive number'));\n return;\n }\n try {\n const res = await createLink({\n variables: {\n input: {\n sessionId,\n permission: interactive ? SessionSharePermission.Interact : SessionSharePermission.View,\n expiresInHours: expiryHours > 0 ? expiryHours : null,\n password: password.trim() ? password.trim() : null,\n maxUses: parsedMax,\n },\n },\n });\n await refetch();\n const url = res.data?.createSessionShareLink.shareUrl;\n if (url) await copy(url, res.data?.createSessionShareLink.id ?? 'new');\n notify.success(\n tr('sessLink.created', 'Share link created'),\n interactive\n ? tr('sessLink.createdInteract', 'Recipient can request keyboard control')\n : tr('sessLink.createdView', 'Recipient can watch the terminal (view-only)')\n );\n setPassword('');\n setMaxUses('');\n } catch (err) {\n notify.error(tr('sessLink.createFailed', 'Could not create link'), (err as Error).message);\n }\n };\n\n const handleRevoke = async (id: string): Promise<void> => {\n try {\n await revokeLink({ variables: { id } });\n await refetch();\n notify.success(tr('sessLink.revoked', 'Link revoked — it no longer works'));\n } catch (err) {\n notify.error(tr('sessLink.revokeFailed', 'Could not revoke'), (err as Error).message);\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4\"\n onClick={onClose}\n >\n <div\n className=\"w-full max-w-lg rounded-lg border border-border-default bg-bg-surface p-5 shadow-lg\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"mb-4 flex items-start justify-between\">\n <div>\n <h3 className=\"flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Link2 className=\"size-5\" />\n {tr('sessLink.title', 'Share link')}\n </h3>\n <p className=\"mt-0.5 text-sm text-text-secondary\">{sessionName}</p>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 text-text-tertiary hover:text-text-primary\"\n aria-label={tr('common.close', 'Close')}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n <div className=\"mb-4 space-y-2\">\n {loading && links.length === 0 ? (\n <LoadingState />\n ) : error ? (\n <ErrorState message={error.message} onRetry={() => void refetch()} />\n ) : activeLinks.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {tr('sessLink.none', 'No share links yet. Create one below.')}\n </p>\n ) : (\n <ul className=\"space-y-2\">\n {activeLinks.map((l) => (\n <li\n key={l.id}\n className=\"flex items-center gap-2 rounded-md border border-border-default px-3 py-2\"\n >\n <Badge>\n {l.permission === SessionSharePermission.Interact ? (\n <>\n <Keyboard className=\"size-3\" /> {tr('sessLink.interact', 'Interactive')}\n </>\n ) : (\n <>\n <Eye className=\"size-3\" /> {tr('sessLink.view', 'View')}\n </>\n )}\n </Badge>\n {l.hasPassword && (\n <Lock className=\"size-3.5 text-text-tertiary\" aria-label=\"password\" />\n )}\n <span className=\"flex-1 truncate font-mono text-xs text-text-secondary\">\n {l.shareUrl}\n </span>\n <span className=\"shrink-0 text-xs text-text-tertiary\">\n {l.useCount}\n {l.maxUses ? `/${l.maxUses}` : ''} {tr('sessLink.uses', 'uses')}\n </span>\n <button\n type=\"button\"\n onClick={() => void copy(l.shareUrl, l.id)}\n className=\"p-1 text-text-secondary hover:text-text-primary\"\n aria-label={tr('common.copy', 'Copy')}\n >\n {copiedId === l.id ? (\n <Check className=\"size-4 text-status-success-text\" />\n ) : (\n <Copy className=\"size-4\" />\n )}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleRevoke(l.id)}\n className=\"p-1 text-text-secondary hover:text-action-danger-text\"\n aria-label={tr('common.revoke', 'Revoke')}\n >\n <Trash2 className=\"size-4\" />\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n\n <form\n onSubmit={(e) => void handleCreate(e)}\n className=\"space-y-3 border-t border-border-default pt-4\"\n >\n <label className=\"flex cursor-pointer items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={interactive}\n onChange={(e) => setInteractive(e.target.checked)}\n className=\"rounded border-border-default\"\n />\n <Keyboard className=\"size-3.5\" />\n {tr(\n 'sessLink.allowInteract',\n 'Allow interactive control (recipient can request the keyboard)'\n )}\n </label>\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n <label className=\"flex-1\">\n <span className=\"text-sm text-text-secondary\">\n {tr('sessLink.expiry', 'Expires')}\n </span>\n <select\n value={expiryHours}\n onChange={(e) => setExpiryHours(Number(e.target.value))}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n {EXPIRY_OPTIONS.map((o) => (\n <option key={o.label} value={o.hours}>\n {o.label}\n </option>\n ))}\n </select>\n </label>\n <label className=\"flex-1\">\n <span className=\"text-sm text-text-secondary\">\n {tr('sessLink.maxUses', 'Max uses (optional)')}\n </span>\n <input\n value={maxUses}\n onChange={(e) => setMaxUses(e.target.value)}\n inputMode=\"numeric\"\n placeholder={tr('sessLink.unlimited', 'unlimited')}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </label>\n </div>\n <label className=\"block\">\n <span className=\"text-sm text-text-secondary\">\n {tr('sessLink.password', 'Password (optional)')}\n </span>\n <input\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n type=\"password\"\n autoComplete=\"new-password\"\n placeholder={tr('sessLink.noPassword', 'no password')}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </label>\n <div className=\"flex justify-end\">\n <button\n type=\"submit\"\n disabled={creating}\n className=\"flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n <Plus className=\"size-4\" />\n {tr('sessLink.create', 'Create share link')}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n\nconst Badge: FC<{ children: ReactNode }> = ({ children }) => (\n <span className=\"inline-flex shrink-0 items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-primary\">\n {children}\n </span>\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionPublicLinkPanel.js","names":[],"sources":["../../../src/components/sessions/SessionPublicLinkPanel.tsx"],"sourcesContent":["import type { FC, FormEvent, ReactNode } from 'react';\nimport { useState } from 'react';\nimport { Link2, Keyboard, Eye, Lock, Copy, Trash2, Plus, X, Check } from 'lucide-react';\n\nimport { LoadingState, ErrorState } from '@/components/shared';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { notify } from '@/lib/toast';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n useGetSessionShareLinksQuery,\n useCreateSessionShareLinkMutation,\n useRevokeSessionShareLinkMutation,\n type SessionShareLinkFieldsFragment,\n} from '@/generated/wspace-operations';\nimport { SessionSharePermission } from '@/generated/wspace-types';\n\ninterface SessionPublicLinkPanelProps {\n sessionId: string;\n sessionName: string;\n onClose: () => void;\n}\n\nconst EXPIRY_OPTIONS = [\n { label: '1 hour', hours: 1 },\n { label: '4 hours', hours: 4 },\n { label: '24 hours', hours: 24 },\n { label: 'No expiry', hours: 0 },\n];\n\n/**\n * SessionPublicLinkPanel — create and manage expiring share links to a live terminal session, with\n * the controls the live-session-share story promises but the generic member-share dialog dropped:\n * viewer vs interactive permission, an **expiry**, an optional **password**, and an optional\n * **usage limit**. Links point at the live `share/:token` page (the real collaborative terminal,\n * not an auth'd accept page). Backed by the existing `createSessionShareLink` mutation — no backend\n * change.\n */\nexport const SessionPublicLinkPanel: FC<SessionPublicLinkPanelProps> = ({\n sessionId,\n sessionName,\n onClose,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const { hasPermission } = usePermissions();\n const canManageLinks = hasPermission(VIBE_PERMISSIONS.SESSION_UPDATE);\n\n const { data, loading, error, refetch } = useGetSessionShareLinksQuery({\n variables: { sessionId },\n fetchPolicy: 'cache-and-network',\n });\n const [createLink, { loading: creating }] = useCreateSessionShareLinkMutation();\n const [revokeLink] = useRevokeSessionShareLinkMutation();\n\n const [interactive, setInteractive] = useState(false);\n const [expiryHours, setExpiryHours] = useState(4);\n const [password, setPassword] = useState('');\n const [maxUses, setMaxUses] = useState('');\n const [copiedId, setCopiedId] = useState<string | null>(null);\n\n const links: SessionShareLinkFieldsFragment[] = data?.sessionShareLinks ?? [];\n const activeLinks = links.filter((l) => l.status === 'ACTIVE');\n\n const copy = async (url: string, id: string): Promise<void> => {\n try {\n await navigator.clipboard.writeText(url);\n setCopiedId(id);\n notify.success(tr('sessLink.copied', 'Link copied'));\n window.setTimeout(() => setCopiedId(null), 1500);\n } catch {\n notify.error(tr('sessLink.copyFailed', 'Could not copy — copy it manually'), url);\n }\n };\n\n const handleCreate = async (e: FormEvent): Promise<void> => {\n e.preventDefault();\n const parsedMax = maxUses.trim() ? Number.parseInt(maxUses.trim(), 10) : null;\n if (parsedMax !== null && (!Number.isFinite(parsedMax) || parsedMax < 1)) {\n notify.error(tr('sessLink.badMax', 'Max uses must be a positive number'));\n return;\n }\n try {\n const res = await createLink({\n variables: {\n input: {\n sessionId,\n permission: interactive ? SessionSharePermission.Interact : SessionSharePermission.View,\n expiresInHours: expiryHours > 0 ? expiryHours : null,\n password: password.trim() ? password.trim() : null,\n maxUses: parsedMax,\n },\n },\n });\n await refetch();\n const url = res.data?.createSessionShareLink.shareUrl;\n if (url) await copy(url, res.data?.createSessionShareLink.id ?? 'new');\n notify.success(\n tr('sessLink.created', 'Share link created'),\n interactive\n ? tr('sessLink.createdInteract', 'Recipient can request keyboard control')\n : tr('sessLink.createdView', 'Recipient can watch the terminal (view-only)')\n );\n setPassword('');\n setMaxUses('');\n } catch (err) {\n notify.error(tr('sessLink.createFailed', 'Could not create link'), (err as Error).message);\n }\n };\n\n const handleRevoke = async (id: string): Promise<void> => {\n try {\n await revokeLink({ variables: { id } });\n await refetch();\n notify.success(tr('sessLink.revoked', 'Link revoked — it no longer works'));\n } catch (err) {\n notify.error(tr('sessLink.revokeFailed', 'Could not revoke'), (err as Error).message);\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4\"\n onClick={onClose}\n >\n <div\n className=\"w-full max-w-lg rounded-lg border border-border-default bg-bg-surface p-5 shadow-lg\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"mb-4 flex items-start justify-between\">\n <div>\n <h3 className=\"flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Link2 className=\"size-5\" />\n {tr('sessLink.title', 'Share link')}\n </h3>\n <p className=\"mt-0.5 text-sm text-text-secondary\">{sessionName}</p>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 text-text-tertiary hover:text-text-primary\"\n aria-label={tr('common.close', 'Close')}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n <div className=\"mb-4 space-y-2\">\n {loading && links.length === 0 ? (\n <LoadingState />\n ) : error ? (\n <ErrorState message={error.message} onRetry={() => void refetch()} />\n ) : activeLinks.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {tr('sessLink.none', 'No share links yet. Create one below.')}\n </p>\n ) : (\n <ul className=\"space-y-2\">\n {activeLinks.map((l) => (\n <li\n key={l.id}\n className=\"flex items-center gap-2 rounded-md border border-border-default px-3 py-2\"\n >\n <Badge>\n {l.permission === SessionSharePermission.Interact ? (\n <>\n <Keyboard className=\"size-3\" /> {tr('sessLink.interact', 'Interactive')}\n </>\n ) : (\n <>\n <Eye className=\"size-3\" /> {tr('sessLink.view', 'View')}\n </>\n )}\n </Badge>\n {l.hasPassword && (\n <Lock className=\"size-3.5 text-text-tertiary\" aria-label=\"password\" />\n )}\n <span className=\"flex-1 truncate font-mono text-xs text-text-secondary\">\n {l.shareUrl}\n </span>\n <span className=\"shrink-0 text-xs text-text-tertiary\">\n {l.useCount}\n {l.maxUses ? `/${l.maxUses}` : ''} {tr('sessLink.uses', 'uses')}\n </span>\n <button\n type=\"button\"\n onClick={() => void copy(l.shareUrl, l.id)}\n className=\"p-1 text-text-secondary hover:text-text-primary\"\n aria-label={tr('common.copy', 'Copy')}\n >\n {copiedId === l.id ? (\n <Check className=\"size-4 text-status-success-text\" />\n ) : (\n <Copy className=\"size-4\" />\n )}\n </button>\n {canManageLinks && (\n <button\n type=\"button\"\n onClick={() => void handleRevoke(l.id)}\n className=\"p-1 text-text-secondary hover:text-action-danger-text\"\n aria-label={tr('common.revoke', 'Revoke')}\n >\n <Trash2 className=\"size-4\" />\n </button>\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n\n {canManageLinks && (\n <form\n onSubmit={(e) => void handleCreate(e)}\n className=\"space-y-3 border-t border-border-default pt-4\"\n >\n <label className=\"flex cursor-pointer items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={interactive}\n onChange={(e) => setInteractive(e.target.checked)}\n className=\"rounded border-border-default\"\n />\n <Keyboard className=\"size-3.5\" />\n {tr(\n 'sessLink.allowInteract',\n 'Allow interactive control (recipient can request the keyboard)'\n )}\n </label>\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n <label className=\"flex-1\">\n <span className=\"text-sm text-text-secondary\">\n {tr('sessLink.expiry', 'Expires')}\n </span>\n <select\n value={expiryHours}\n onChange={(e) => setExpiryHours(Number(e.target.value))}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n {EXPIRY_OPTIONS.map((o) => (\n <option key={o.label} value={o.hours}>\n {o.label}\n </option>\n ))}\n </select>\n </label>\n <label className=\"flex-1\">\n <span className=\"text-sm text-text-secondary\">\n {tr('sessLink.maxUses', 'Max uses (optional)')}\n </span>\n <input\n value={maxUses}\n onChange={(e) => setMaxUses(e.target.value)}\n inputMode=\"numeric\"\n placeholder={tr('sessLink.unlimited', 'unlimited')}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </label>\n </div>\n <label className=\"block\">\n <span className=\"text-sm text-text-secondary\">\n {tr('sessLink.password', 'Password (optional)')}\n </span>\n <input\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n type=\"password\"\n autoComplete=\"new-password\"\n placeholder={tr('sessLink.noPassword', 'no password')}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </label>\n <div className=\"flex justify-end\">\n <button\n type=\"submit\"\n disabled={creating}\n className=\"flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n <Plus className=\"size-4\" />\n {tr('sessLink.create', 'Create share link')}\n </button>\n </div>\n </form>\n )}\n </div>\n </div>\n );\n};\n\nconst Badge: FC<{ children: ReactNode }> = ({ children }) => (\n <span className=\"inline-flex shrink-0 items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-primary\">\n {children}\n </span>\n);\n"],"mappings":"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"}
|
|
@@ -1,171 +1,173 @@
|
|
|
1
1
|
import { useDeleteSettingsSecretMutation as e, useSetSettingsSecretMutation as t } from "../../generated/wspace-operations.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
2
|
+
import { VIBE_PERMISSIONS as n } from "../../constants/permissions.js";
|
|
3
|
+
import { SectionCard as r, SelectField as i, ToggleField as a } from "./SettingsForm.js";
|
|
4
|
+
import { useCallback as o, useMemo as s, useState as c } from "react";
|
|
5
|
+
import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
6
|
+
import { Lock as u, Plus as d, RefreshCw as f, Trash2 as p } from "lucide-react";
|
|
7
|
+
import { usePermissions as m } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
8
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
9
|
+
import { MotionItem as _, MotionList as v } from "@burdenoff/fe-libs/shared/motion";
|
|
8
10
|
//#region src/components/settings/AdvancedSection.tsx
|
|
9
|
-
function
|
|
10
|
-
let { t:
|
|
11
|
-
let n =
|
|
11
|
+
function y({ data: y, onChange: b, secrets: x, secretsLoading: S, onRefreshSecrets: C, workspaceId: w }) {
|
|
12
|
+
let { t: T } = l(), { hasPermission: E } = m(), D = E(n.CONFIG_UPDATE), O = E(n.CONFIG_DELETE), k = o((e, t) => {
|
|
13
|
+
let n = T(e);
|
|
12
14
|
return n === e ? t : n;
|
|
13
|
-
}, [
|
|
15
|
+
}, [T]), [A, j] = c(""), [M, N] = c(""), [P, F] = c(!1), I = s(() => [
|
|
14
16
|
{
|
|
15
17
|
value: "debug",
|
|
16
|
-
label:
|
|
18
|
+
label: k("settings.logLevelDebug", "Debug")
|
|
17
19
|
},
|
|
18
20
|
{
|
|
19
21
|
value: "info",
|
|
20
|
-
label:
|
|
22
|
+
label: k("settings.logLevelInfo", "Info")
|
|
21
23
|
},
|
|
22
24
|
{
|
|
23
25
|
value: "warn",
|
|
24
|
-
label:
|
|
26
|
+
label: k("settings.logLevelWarning", "Warning")
|
|
25
27
|
},
|
|
26
28
|
{
|
|
27
29
|
value: "error",
|
|
28
|
-
label:
|
|
30
|
+
label: k("common.error", "Error")
|
|
29
31
|
}
|
|
30
|
-
], [
|
|
31
|
-
|
|
32
|
-
} }), [
|
|
33
|
-
!
|
|
32
|
+
], [k]), [L] = t({ onCompleted: () => {
|
|
33
|
+
j(""), N(""), F(!1), C();
|
|
34
|
+
} }), [R] = e({ onCompleted: () => C() }), z = async () => {
|
|
35
|
+
!A.trim() || !M.trim() || await L({ variables: {
|
|
34
36
|
scope: "WORKSPACE",
|
|
35
|
-
scopeId:
|
|
36
|
-
key:
|
|
37
|
-
value:
|
|
37
|
+
scopeId: w,
|
|
38
|
+
key: A.trim(),
|
|
39
|
+
value: M.trim()
|
|
38
40
|
} });
|
|
39
|
-
},
|
|
40
|
-
await
|
|
41
|
+
}, B = async (e) => {
|
|
42
|
+
await R({ variables: {
|
|
41
43
|
scope: "WORKSPACE",
|
|
42
|
-
scopeId:
|
|
44
|
+
scopeId: w,
|
|
43
45
|
key: e
|
|
44
46
|
} });
|
|
45
47
|
};
|
|
46
|
-
return /* @__PURE__ */
|
|
48
|
+
return /* @__PURE__ */ g("div", {
|
|
47
49
|
className: "space-y-6",
|
|
48
|
-
children: [/* @__PURE__ */
|
|
49
|
-
title:
|
|
50
|
+
children: [/* @__PURE__ */ g(r, {
|
|
51
|
+
title: k("settings.advancedSettings", "Advanced Settings"),
|
|
50
52
|
children: [
|
|
51
|
-
/* @__PURE__ */
|
|
52
|
-
label:
|
|
53
|
-
value:
|
|
54
|
-
onChange: (e) =>
|
|
55
|
-
...
|
|
53
|
+
/* @__PURE__ */ h(i, {
|
|
54
|
+
label: k("settings.logLevel", "Log Level"),
|
|
55
|
+
value: y.logLevel,
|
|
56
|
+
onChange: (e) => b({
|
|
57
|
+
...y,
|
|
56
58
|
logLevel: e
|
|
57
59
|
}),
|
|
58
|
-
options:
|
|
60
|
+
options: I
|
|
59
61
|
}),
|
|
60
|
-
/* @__PURE__ */
|
|
61
|
-
label:
|
|
62
|
-
description:
|
|
63
|
-
value:
|
|
64
|
-
onChange: (e) =>
|
|
65
|
-
...
|
|
62
|
+
/* @__PURE__ */ h(a, {
|
|
63
|
+
label: k("settings.telemetry", "Telemetry"),
|
|
64
|
+
description: k("settings.telemetryDescription", "Help improve VibeControls by sending anonymous usage data"),
|
|
65
|
+
value: y.telemetryEnabled,
|
|
66
|
+
onChange: (e) => b({
|
|
67
|
+
...y,
|
|
66
68
|
telemetryEnabled: e
|
|
67
69
|
})
|
|
68
70
|
}),
|
|
69
|
-
/* @__PURE__ */
|
|
70
|
-
label:
|
|
71
|
-
description:
|
|
72
|
-
value:
|
|
73
|
-
onChange: (e) =>
|
|
74
|
-
...
|
|
71
|
+
/* @__PURE__ */ h(a, {
|
|
72
|
+
label: k("settings.autoUpdate", "Auto Update"),
|
|
73
|
+
description: k("settings.autoUpdateDescription", "Automatically update agents when new versions are available"),
|
|
74
|
+
value: y.autoUpdate,
|
|
75
|
+
onChange: (e) => b({
|
|
76
|
+
...y,
|
|
75
77
|
autoUpdate: e
|
|
76
78
|
})
|
|
77
79
|
})
|
|
78
80
|
]
|
|
79
|
-
}), /* @__PURE__ */
|
|
81
|
+
}), /* @__PURE__ */ g("div", {
|
|
80
82
|
className: "bg-bg-surface border border-border-default rounded-lg p-6",
|
|
81
83
|
children: [
|
|
82
|
-
/* @__PURE__ */
|
|
84
|
+
/* @__PURE__ */ g("div", {
|
|
83
85
|
className: "flex items-center justify-between mb-4",
|
|
84
|
-
children: [/* @__PURE__ */
|
|
86
|
+
children: [/* @__PURE__ */ h("h3", {
|
|
85
87
|
className: "font-medium text-text-primary",
|
|
86
|
-
children:
|
|
87
|
-
}), /* @__PURE__ */
|
|
88
|
+
children: k("settings.configuredSecrets", "Configured Secrets")
|
|
89
|
+
}), /* @__PURE__ */ g("div", {
|
|
88
90
|
className: "flex items-center gap-2",
|
|
89
|
-
children: [/* @__PURE__ */
|
|
91
|
+
children: [D && /* @__PURE__ */ h("button", {
|
|
90
92
|
type: "button",
|
|
91
|
-
onClick: () =>
|
|
93
|
+
onClick: () => F(!P),
|
|
92
94
|
className: "p-1 hover:bg-bg-sunken rounded-md transition-colors",
|
|
93
|
-
title:
|
|
94
|
-
children: /* @__PURE__ */
|
|
95
|
-
}), /* @__PURE__ */
|
|
95
|
+
title: k("settings.addSecret", "Add secret"),
|
|
96
|
+
children: /* @__PURE__ */ h(d, { className: "size-4 text-text-secondary" })
|
|
97
|
+
}), /* @__PURE__ */ h("button", {
|
|
96
98
|
type: "button",
|
|
97
|
-
onClick:
|
|
98
|
-
disabled:
|
|
99
|
+
onClick: C,
|
|
100
|
+
disabled: S,
|
|
99
101
|
className: "p-1 hover:bg-bg-sunken rounded-md transition-colors",
|
|
100
|
-
title:
|
|
101
|
-
children: /* @__PURE__ */
|
|
102
|
+
title: k("settings.refreshSecrets", "Refresh secrets"),
|
|
103
|
+
children: /* @__PURE__ */ h(f, { className: `size-4 text-text-secondary ${S ? "animate-spin" : ""}` })
|
|
102
104
|
})]
|
|
103
105
|
})]
|
|
104
106
|
}),
|
|
105
|
-
|
|
107
|
+
P && /* @__PURE__ */ g("div", {
|
|
106
108
|
className: "mb-4 p-4 bg-bg-sunken rounded-md space-y-3",
|
|
107
109
|
children: [
|
|
108
|
-
/* @__PURE__ */
|
|
110
|
+
/* @__PURE__ */ h("input", {
|
|
109
111
|
"aria-label": "control",
|
|
110
112
|
type: "text",
|
|
111
|
-
placeholder:
|
|
112
|
-
value:
|
|
113
|
-
onChange: (e) =>
|
|
113
|
+
placeholder: k("settings.secretKeyPlaceholder", "Secret key (e.g., api_key, database_url)"),
|
|
114
|
+
value: A,
|
|
115
|
+
onChange: (e) => j(e.target.value),
|
|
114
116
|
className: "w-full px-3 py-2 bg-bg-surface border border-border-default rounded-md text-text-primary text-sm"
|
|
115
117
|
}),
|
|
116
|
-
/* @__PURE__ */
|
|
118
|
+
/* @__PURE__ */ h("input", {
|
|
117
119
|
"aria-label": "control",
|
|
118
120
|
type: "password",
|
|
119
|
-
placeholder:
|
|
120
|
-
value:
|
|
121
|
-
onChange: (e) =>
|
|
121
|
+
placeholder: k("settings.secretValuePlaceholder", "Secret value"),
|
|
122
|
+
value: M,
|
|
123
|
+
onChange: (e) => N(e.target.value),
|
|
122
124
|
className: "w-full px-3 py-2 bg-bg-surface border border-border-default rounded-md text-text-primary text-sm"
|
|
123
125
|
}),
|
|
124
|
-
/* @__PURE__ */
|
|
126
|
+
/* @__PURE__ */ g("div", {
|
|
125
127
|
className: "flex justify-end gap-2",
|
|
126
|
-
children: [/* @__PURE__ */
|
|
128
|
+
children: [/* @__PURE__ */ h("button", {
|
|
127
129
|
type: "button",
|
|
128
|
-
onClick: () =>
|
|
130
|
+
onClick: () => F(!1),
|
|
129
131
|
className: "px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary",
|
|
130
|
-
children:
|
|
131
|
-
}), /* @__PURE__ */
|
|
132
|
+
children: k("common.cancel", "Cancel")
|
|
133
|
+
}), /* @__PURE__ */ h("button", {
|
|
132
134
|
type: "button",
|
|
133
|
-
onClick:
|
|
134
|
-
disabled: !
|
|
135
|
+
onClick: z,
|
|
136
|
+
disabled: !A.trim() || !M.trim(),
|
|
135
137
|
className: "px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover disabled:opacity-50",
|
|
136
|
-
children:
|
|
138
|
+
children: k("settings.saveSecret", "Save Secret")
|
|
137
139
|
})]
|
|
138
140
|
})
|
|
139
141
|
]
|
|
140
142
|
}),
|
|
141
|
-
/* @__PURE__ */
|
|
143
|
+
/* @__PURE__ */ g(v, {
|
|
142
144
|
stagger: .04,
|
|
143
145
|
className: "space-y-3",
|
|
144
|
-
children: [
|
|
146
|
+
children: [x.map((e) => /* @__PURE__ */ g(_, {
|
|
145
147
|
className: "flex items-center justify-between p-3 bg-bg-sunken rounded-md",
|
|
146
|
-
children: [/* @__PURE__ */
|
|
148
|
+
children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("div", {
|
|
147
149
|
className: "font-medium text-text-primary",
|
|
148
150
|
children: e.key
|
|
149
|
-
}), /* @__PURE__ */
|
|
151
|
+
}), /* @__PURE__ */ g("div", {
|
|
150
152
|
className: "flex items-center gap-1.5 text-sm text-text-secondary font-mono",
|
|
151
|
-
title:
|
|
152
|
-
children: [/* @__PURE__ */
|
|
153
|
+
title: k("settings.secretHidden", "Write-only — secret values are stored encrypted and never returned"),
|
|
154
|
+
children: [/* @__PURE__ */ h(u, {
|
|
153
155
|
className: "size-3",
|
|
154
156
|
"aria-hidden": "true"
|
|
155
157
|
}), "••••••••"]
|
|
156
|
-
})] }), /* @__PURE__ */
|
|
158
|
+
})] }), /* @__PURE__ */ h("div", {
|
|
157
159
|
className: "flex items-center gap-1",
|
|
158
|
-
children: /* @__PURE__ */
|
|
160
|
+
children: O && /* @__PURE__ */ h("button", {
|
|
159
161
|
type: "button",
|
|
160
|
-
onClick: () =>
|
|
162
|
+
onClick: () => B(e.key),
|
|
161
163
|
className: "p-2 hover:bg-bg-surface rounded-md transition-colors text-status-error-text",
|
|
162
|
-
title:
|
|
163
|
-
children: /* @__PURE__ */ p
|
|
164
|
+
title: k("settings.deleteSecret", "Delete secret"),
|
|
165
|
+
children: /* @__PURE__ */ h(p, { className: "size-4" })
|
|
164
166
|
})
|
|
165
167
|
})]
|
|
166
|
-
}, e.id)),
|
|
168
|
+
}, e.id)), x.length === 0 && !S && /* @__PURE__ */ h("div", {
|
|
167
169
|
className: "text-sm text-text-secondary py-4 text-center",
|
|
168
|
-
children:
|
|
170
|
+
children: k("settings.noSecretsConfigured", "No secrets configured")
|
|
169
171
|
})]
|
|
170
172
|
})
|
|
171
173
|
]
|
|
@@ -173,6 +175,6 @@ function _({ data: _, onChange: v, secrets: y, secretsLoading: b, onRefreshSecre
|
|
|
173
175
|
});
|
|
174
176
|
}
|
|
175
177
|
//#endregion
|
|
176
|
-
export {
|
|
178
|
+
export { y as AdvancedSection };
|
|
177
179
|
|
|
178
180
|
//# sourceMappingURL=AdvancedSection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdvancedSection.js","names":[],"sources":["../../../src/components/settings/AdvancedSection.tsx"],"sourcesContent":["import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Lock, RefreshCw, Plus, Trash2 } from 'lucide-react';\nimport { useCallback, useMemo, useState } from 'react';\nimport { SectionCard, SelectField, ToggleField } from './SettingsForm';\nimport { MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport {\n useSetSettingsSecretMutation,\n useDeleteSettingsSecretMutation,\n} from '@/generated/wspace-operations';\n\ninterface ConfigurationItem {\n id: string;\n key: string;\n value: Record<string, unknown>;\n isSecret: boolean;\n}\n\ninterface AdvancedData {\n logLevel: string;\n telemetryEnabled: boolean;\n autoUpdate: boolean;\n}\n\ninterface AdvancedSectionProps {\n data: AdvancedData;\n onChange: (data: AdvancedData) => void;\n secrets: ConfigurationItem[];\n secretsLoading: boolean;\n onRefreshSecrets: () => void;\n workspaceId: string;\n}\n\nexport function AdvancedSection({\n data,\n onChange,\n secrets,\n secretsLoading,\n onRefreshSecrets,\n workspaceId,\n}: AdvancedSectionProps) {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n },\n [t]\n );\n const [newSecretKey, setNewSecretKey] = useState('');\n const [newSecretValue, setNewSecretValue] = useState('');\n const [isAddingSecret, setIsAddingSecret] = useState(false);\n const logLevelOptions = useMemo(\n () => [\n { value: 'debug', label: tr('settings.logLevelDebug', 'Debug') },\n { value: 'info', label: tr('settings.logLevelInfo', 'Info') },\n { value: 'warn', label: tr('settings.logLevelWarning', 'Warning') },\n { value: 'error', label: tr('common.error', 'Error') },\n ],\n [tr]\n );\n\n const [setSecretMutation] = useSetSettingsSecretMutation({\n onCompleted: () => {\n setNewSecretKey('');\n setNewSecretValue('');\n setIsAddingSecret(false);\n onRefreshSecrets();\n },\n });\n\n const [deleteSecretMutation] = useDeleteSettingsSecretMutation({\n onCompleted: () => onRefreshSecrets(),\n });\n\n const handleAddSecret = async () => {\n if (!newSecretKey.trim() || !newSecretValue.trim()) return;\n await setSecretMutation({\n variables: {\n scope: 'WORKSPACE' as never,\n scopeId: workspaceId,\n key: newSecretKey.trim(),\n value: newSecretValue.trim(),\n },\n });\n };\n\n const handleDeleteSecret = async (key: string) => {\n await deleteSecretMutation({\n variables: {\n scope: 'WORKSPACE' as never,\n scopeId: workspaceId,\n key,\n },\n });\n };\n\n return (\n <div className=\"space-y-6\">\n <SectionCard title={tr('settings.advancedSettings', 'Advanced Settings')}>\n <SelectField\n label={tr('settings.logLevel', 'Log Level')}\n value={data.logLevel}\n onChange={(v) => onChange({ ...data, logLevel: v })}\n options={logLevelOptions}\n />\n <ToggleField\n label={tr('settings.telemetry', 'Telemetry')}\n description={tr(\n 'settings.telemetryDescription',\n 'Help improve VibeControls by sending anonymous usage data'\n )}\n value={data.telemetryEnabled}\n onChange={(v) => onChange({ ...data, telemetryEnabled: v })}\n />\n <ToggleField\n label={tr('settings.autoUpdate', 'Auto Update')}\n description={tr(\n 'settings.autoUpdateDescription',\n 'Automatically update agents when new versions are available'\n )}\n value={data.autoUpdate}\n onChange={(v) => onChange({ ...data, autoUpdate: v })}\n />\n </SectionCard>\n\n {/* Secrets Section */}\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-6\">\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"font-medium text-text-primary\">\n {tr('settings.configuredSecrets', 'Configured Secrets')}\n </h3>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setIsAddingSecret(!isAddingSecret)}\n className=\"p-1 hover:bg-bg-sunken rounded-md transition-colors\"\n title={tr('settings.addSecret', 'Add secret')}\n >\n <Plus className=\"size-4 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={onRefreshSecrets}\n disabled={secretsLoading}\n className=\"p-1 hover:bg-bg-sunken rounded-md transition-colors\"\n title={tr('settings.refreshSecrets', 'Refresh secrets')}\n >\n <RefreshCw\n className={`size-4 text-text-secondary ${secretsLoading ? 'animate-spin' : ''}`}\n />\n </button>\n </div>\n </div>\n\n {/* Add Secret Form */}\n {isAddingSecret && (\n <div className=\"mb-4 p-4 bg-bg-sunken rounded-md space-y-3\">\n <input\n aria-label=\"control\"\n type=\"text\"\n placeholder={tr(\n 'settings.secretKeyPlaceholder',\n 'Secret key (e.g., api_key, database_url)'\n )}\n value={newSecretKey}\n onChange={(e) => setNewSecretKey(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-surface border border-border-default rounded-md text-text-primary text-sm\"\n />\n <input\n aria-label=\"control\"\n type=\"password\"\n placeholder={tr('settings.secretValuePlaceholder', 'Secret value')}\n value={newSecretValue}\n onChange={(e) => setNewSecretValue(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-surface border border-border-default rounded-md text-text-primary text-sm\"\n />\n <div className=\"flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => setIsAddingSecret(false)}\n className=\"px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleAddSecret}\n disabled={!newSecretKey.trim() || !newSecretValue.trim()}\n className=\"px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {tr('settings.saveSecret', 'Save Secret')}\n </button>\n </div>\n </div>\n )}\n\n <MotionList stagger={0.04} className=\"space-y-3\">\n {secrets.map((config) => (\n <MotionItem\n key={config.id}\n className=\"flex items-center justify-between p-3 bg-bg-sunken rounded-md\"\n >\n <div>\n <div className=\"font-medium text-text-primary\">{config.key}</div>\n {/* Secret values are write-only \\u2014 the API redacts them ('********')\n and never returns plaintext, so there is nothing to \"reveal\".\n Show a fixed mask + an explanatory tooltip instead of a\n misleading eye toggle that appeared to do nothing. */}\n <div\n className=\"flex items-center gap-1.5 text-sm text-text-secondary font-mono\"\n title={tr(\n 'settings.secretHidden',\n 'Write-only \\u2014 secret values are stored encrypted and never returned'\n )}\n >\n <Lock className=\"size-3\" aria-hidden=\"true\" />\n {'\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022'}\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => handleDeleteSecret(config.key)}\n className=\"p-2 hover:bg-bg-surface rounded-md transition-colors text-status-error-text\"\n title={tr('settings.deleteSecret', 'Delete secret')}\n >\n <Trash2 className=\"size-4\" />\n </button>\n </div>\n </MotionItem>\n ))}\n {secrets.length === 0 && !secretsLoading && (\n <div className=\"text-sm text-text-secondary py-4 text-center\">\n {tr('settings.noSecretsConfigured', 'No secrets configured')}\n </div>\n )}\n </MotionList>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdvancedSection.js","names":[],"sources":["../../../src/components/settings/AdvancedSection.tsx"],"sourcesContent":["import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Lock, RefreshCw, Plus, Trash2 } from 'lucide-react';\nimport { useCallback, useMemo, useState } from 'react';\nimport { SectionCard, SelectField, ToggleField } from './SettingsForm';\nimport { MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport {\n useSetSettingsSecretMutation,\n useDeleteSettingsSecretMutation,\n} from '@/generated/wspace-operations';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\n\ninterface ConfigurationItem {\n id: string;\n key: string;\n value: Record<string, unknown>;\n isSecret: boolean;\n}\n\ninterface AdvancedData {\n logLevel: string;\n telemetryEnabled: boolean;\n autoUpdate: boolean;\n}\n\ninterface AdvancedSectionProps {\n data: AdvancedData;\n onChange: (data: AdvancedData) => void;\n secrets: ConfigurationItem[];\n secretsLoading: boolean;\n onRefreshSecrets: () => void;\n workspaceId: string;\n}\n\nexport function AdvancedSection({\n data,\n onChange,\n secrets,\n secretsLoading,\n onRefreshSecrets,\n workspaceId,\n}: AdvancedSectionProps) {\n const { t } = useI18n();\n const { hasPermission } = usePermissions();\n // setSettingsSecret → @rbac(action:\"update\", resource:\"configuration\");\n // deleteSettingsSecret → @rbac(action:\"delete\", resource:\"configuration\").\n const canManageSecrets = hasPermission(VIBE_PERMISSIONS.CONFIG_UPDATE);\n const canDeleteSecret = hasPermission(VIBE_PERMISSIONS.CONFIG_DELETE);\n const tr = useCallback(\n (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n },\n [t]\n );\n const [newSecretKey, setNewSecretKey] = useState('');\n const [newSecretValue, setNewSecretValue] = useState('');\n const [isAddingSecret, setIsAddingSecret] = useState(false);\n const logLevelOptions = useMemo(\n () => [\n { value: 'debug', label: tr('settings.logLevelDebug', 'Debug') },\n { value: 'info', label: tr('settings.logLevelInfo', 'Info') },\n { value: 'warn', label: tr('settings.logLevelWarning', 'Warning') },\n { value: 'error', label: tr('common.error', 'Error') },\n ],\n [tr]\n );\n\n const [setSecretMutation] = useSetSettingsSecretMutation({\n onCompleted: () => {\n setNewSecretKey('');\n setNewSecretValue('');\n setIsAddingSecret(false);\n onRefreshSecrets();\n },\n });\n\n const [deleteSecretMutation] = useDeleteSettingsSecretMutation({\n onCompleted: () => onRefreshSecrets(),\n });\n\n const handleAddSecret = async () => {\n if (!newSecretKey.trim() || !newSecretValue.trim()) return;\n await setSecretMutation({\n variables: {\n scope: 'WORKSPACE' as never,\n scopeId: workspaceId,\n key: newSecretKey.trim(),\n value: newSecretValue.trim(),\n },\n });\n };\n\n const handleDeleteSecret = async (key: string) => {\n await deleteSecretMutation({\n variables: {\n scope: 'WORKSPACE' as never,\n scopeId: workspaceId,\n key,\n },\n });\n };\n\n return (\n <div className=\"space-y-6\">\n <SectionCard title={tr('settings.advancedSettings', 'Advanced Settings')}>\n <SelectField\n label={tr('settings.logLevel', 'Log Level')}\n value={data.logLevel}\n onChange={(v) => onChange({ ...data, logLevel: v })}\n options={logLevelOptions}\n />\n <ToggleField\n label={tr('settings.telemetry', 'Telemetry')}\n description={tr(\n 'settings.telemetryDescription',\n 'Help improve VibeControls by sending anonymous usage data'\n )}\n value={data.telemetryEnabled}\n onChange={(v) => onChange({ ...data, telemetryEnabled: v })}\n />\n <ToggleField\n label={tr('settings.autoUpdate', 'Auto Update')}\n description={tr(\n 'settings.autoUpdateDescription',\n 'Automatically update agents when new versions are available'\n )}\n value={data.autoUpdate}\n onChange={(v) => onChange({ ...data, autoUpdate: v })}\n />\n </SectionCard>\n\n {/* Secrets Section */}\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-6\">\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"font-medium text-text-primary\">\n {tr('settings.configuredSecrets', 'Configured Secrets')}\n </h3>\n <div className=\"flex items-center gap-2\">\n {canManageSecrets && (\n <button\n type=\"button\"\n onClick={() => setIsAddingSecret(!isAddingSecret)}\n className=\"p-1 hover:bg-bg-sunken rounded-md transition-colors\"\n title={tr('settings.addSecret', 'Add secret')}\n >\n <Plus className=\"size-4 text-text-secondary\" />\n </button>\n )}\n <button\n type=\"button\"\n onClick={onRefreshSecrets}\n disabled={secretsLoading}\n className=\"p-1 hover:bg-bg-sunken rounded-md transition-colors\"\n title={tr('settings.refreshSecrets', 'Refresh secrets')}\n >\n <RefreshCw\n className={`size-4 text-text-secondary ${secretsLoading ? 'animate-spin' : ''}`}\n />\n </button>\n </div>\n </div>\n\n {/* Add Secret Form */}\n {isAddingSecret && (\n <div className=\"mb-4 p-4 bg-bg-sunken rounded-md space-y-3\">\n <input\n aria-label=\"control\"\n type=\"text\"\n placeholder={tr(\n 'settings.secretKeyPlaceholder',\n 'Secret key (e.g., api_key, database_url)'\n )}\n value={newSecretKey}\n onChange={(e) => setNewSecretKey(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-surface border border-border-default rounded-md text-text-primary text-sm\"\n />\n <input\n aria-label=\"control\"\n type=\"password\"\n placeholder={tr('settings.secretValuePlaceholder', 'Secret value')}\n value={newSecretValue}\n onChange={(e) => setNewSecretValue(e.target.value)}\n className=\"w-full px-3 py-2 bg-bg-surface border border-border-default rounded-md text-text-primary text-sm\"\n />\n <div className=\"flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => setIsAddingSecret(false)}\n className=\"px-3 py-1.5 text-sm text-text-secondary hover:text-text-primary\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleAddSecret}\n disabled={!newSecretKey.trim() || !newSecretValue.trim()}\n className=\"px-3 py-1.5 text-sm bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {tr('settings.saveSecret', 'Save Secret')}\n </button>\n </div>\n </div>\n )}\n\n <MotionList stagger={0.04} className=\"space-y-3\">\n {secrets.map((config) => (\n <MotionItem\n key={config.id}\n className=\"flex items-center justify-between p-3 bg-bg-sunken rounded-md\"\n >\n <div>\n <div className=\"font-medium text-text-primary\">{config.key}</div>\n {/* Secret values are write-only \\u2014 the API redacts them ('********')\n and never returns plaintext, so there is nothing to \"reveal\".\n Show a fixed mask + an explanatory tooltip instead of a\n misleading eye toggle that appeared to do nothing. */}\n <div\n className=\"flex items-center gap-1.5 text-sm text-text-secondary font-mono\"\n title={tr(\n 'settings.secretHidden',\n 'Write-only \\u2014 secret values are stored encrypted and never returned'\n )}\n >\n <Lock className=\"size-3\" aria-hidden=\"true\" />\n {'\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022'}\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n {canDeleteSecret && (\n <button\n type=\"button\"\n onClick={() => handleDeleteSecret(config.key)}\n className=\"p-2 hover:bg-bg-surface rounded-md transition-colors text-status-error-text\"\n title={tr('settings.deleteSecret', 'Delete secret')}\n >\n <Trash2 className=\"size-4\" />\n </button>\n )}\n </div>\n </MotionItem>\n ))}\n {secrets.length === 0 && !secretsLoading && (\n <div className=\"text-sm text-text-secondary py-4 text-center\">\n {tr('settings.noSecretsConfigured', 'No secrets configured')}\n </div>\n )}\n </MotionList>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|