@burdenoff/microfe-vibecontrols 2026.531.12 → 2026.531.14
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/gitops/VibeGitOpsTab.js +3 -3
- package/dist/components/gitops/VibeGitOpsTab.js.map +1 -1
- package/dist/components/sessions/SessionShareDialog.js +63 -65
- package/dist/components/sessions/SessionShareDialog.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckShareDialog.js +144 -132
- package/dist/components/vibedeck/VibeDeckShareDialog.js.map +1 -1
- package/dist/services/iframeToken.js +18 -18
- package/dist/services/iframeToken.js.map +1 -1
- package/dist/utils/shareUrl.js +41 -0
- package/dist/utils/shareUrl.js.map +1 -0
- package/package.json +1 -1
|
@@ -105,10 +105,10 @@ var x = ({ vibeId: x, agentId: w, profile: T, tunnelUrl: E, tr: D }) => {
|
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
107
|
try {
|
|
108
|
-
let a = await r(w, T, "/
|
|
108
|
+
let a = await r(w, T, "/git");
|
|
109
109
|
if (e) return;
|
|
110
|
-
let o = `${E.replace(/\/$/, "")}/
|
|
111
|
-
Q(n(o, a.token)), t = i(w, T, "/
|
|
110
|
+
let o = `${E.replace(/\/$/, "")}/git/`;
|
|
111
|
+
Q(n(o, a.token)), t = i(w, T, "/git", a, (e) => {
|
|
112
112
|
Q(n(o, e.token));
|
|
113
113
|
});
|
|
114
114
|
} catch (t) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VibeGitOpsTab.js","names":[],"sources":["../../../src/components/gitops/VibeGitOpsTab.tsx"],"sourcesContent":["/**\n * VibeGitOpsTab — GitOps tab content for the Vibe detail page.\n *\n * V2: All data is sourced from the agent over the tunnel via\n * `/api/profiles/<profile>/gitops/<provider>/*`. No backend GraphQL\n * involvement. The backend's old `gitopsProvider` records are deprecated.\n *\n * Sub-tabs:\n * 1. Repo Stats — repo metadata + PR + issue + security counts\n * 2. CI Pipelines — recent runs + analytics percentiles\n * 3. Git UI — Ungit iframe (when vibe-plugin-tool-git is installed)\n */\nimport { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport { motion } from 'framer-motion';\nimport { GitMerge, BarChart3, Play, GitBranch, RefreshCw } from 'lucide-react';\nimport { useQueryClient } from '@tanstack/react-query';\n\nimport { RepoStatsTab } from './RepoStatsTab';\nimport { CITab } from './CITab';\nimport { GitOpsSetup } from './GitOpsSetup';\nimport { GitOpsProviderPicker } from './GitOpsProviderPicker';\nimport { PluginGate } from '@/components/shared';\nimport { GIT_PACKAGE } from '@/constants/pluginCatalog';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\nimport {\n useGitopsContributors,\n useGitopsIssues,\n useGitopsLanguages,\n useGitopsPipelineAnalytics,\n useGitopsProviders,\n useGitopsPullRequests,\n useGitopsRepos,\n useGitopsRuns,\n useGitopsSecurityAlerts,\n} from '@/hooks/useGitops';\nimport { composeCiStatus, composeRepoStats, type ProviderName, type RepoFqn } from './types';\nimport {\n mintIframeToken,\n scheduleIframeTokenRefresh,\n appendIframeTokenQuery,\n type IframeTokenAutoRefreshHandle,\n type IframeTokenResponse,\n} from '@/services/iframeToken';\n\ninterface VibeGitOpsTabProps {\n vibeId: string;\n agentId: string;\n /** Canonical agent profile name. */\n profile: string;\n /**\n * Tunnel URL — retained ONLY for the `git-ui` iframe handshake\n * (ungit subpath) which still bootstraps via a one-time iframe token.\n * GitOps REST calls no longer use this; they route through the svc\n * proxy (F4 Wave C).\n */\n tunnelUrl: string | null;\n tr: (key: string, fallback: string) => string;\n}\n\ntype GitOpsSubTab = 'repo-stats' | 'ci' | 'git-ui';\n\nexport const VibeGitOpsTab: FC<VibeGitOpsTabProps> = ({\n vibeId,\n agentId,\n profile,\n tunnelUrl,\n tr,\n}) => {\n const [subTab, setSubTab] = useState<GitOpsSubTab>('repo-stats');\n const [showSetup, setShowSetup] = useState(false);\n const [selectedProvider, setSelectedProvider] = useState<ProviderName | null>(null);\n const [selectedRepoFqn, setSelectedRepoFqn] = useState<RepoFqn | null>(null);\n const { workspaceId } = useVibeControls();\n const qc = useQueryClient();\n\n const ctx = useMemo(\n () => ({ profile, workspaceId: workspaceId ?? '', agentId }),\n [profile, workspaceId, agentId]\n );\n\n // Providers registered on the agent.\n const providersQ = useGitopsProviders(ctx);\n const authedProviders = useMemo(\n () => providersQ.data?.filter((p) => p.registered && p.authenticated) ?? [],\n [providersQ.data]\n );\n const hasProviders = authedProviders.length > 0;\n\n // Pick a default once data loads.\n useEffect(() => {\n if (!selectedProvider && authedProviders.length > 0) {\n const first = authedProviders[0];\n if (first) {\n setSelectedProvider(first.name as ProviderName);\n }\n }\n }, [authedProviders, selectedProvider]);\n\n // Once a provider is selected, fetch repos for it and pick the first one\n // by default. The user can switch repos via the picker.\n const reposQ = useGitopsRepos(ctx, selectedProvider, { limit: 30 });\n useEffect(() => {\n if (!selectedRepoFqn && reposQ.data?.items.length) {\n const first = reposQ.data.items[0];\n if (first) {\n setSelectedRepoFqn(first.fqn);\n }\n }\n }, [reposQ.data, selectedRepoFqn]);\n\n // Repo Stats composite — runs only when active sub-tab is repo-stats.\n const repoQ = useGitopsRepos(ctx, selectedProvider, { limit: 1 }); // metadata via list (1st item)\n const repoDetailQ = useGitopsRepos.length ? null : null; // keep TS happy; we use repoQ.items[0]\n const pullsQ = useGitopsPullRequests(ctx, selectedProvider, selectedRepoFqn, {\n state: 'open',\n limit: 100,\n });\n const issuesQ = useGitopsIssues(ctx, selectedProvider, selectedRepoFqn, {\n state: 'open',\n limit: 100,\n });\n const languagesQ = useGitopsLanguages(ctx, selectedProvider, selectedRepoFqn);\n const contributorsQ = useGitopsContributors(ctx, selectedProvider, selectedRepoFqn, 20);\n const securityQ = useGitopsSecurityAlerts(ctx, selectedProvider, selectedRepoFqn);\n\n const activeRepo = useMemo(\n () =>\n selectedRepoFqn\n ? (repoQ.data?.items.find((r) => r.fqn === selectedRepoFqn) ??\n reposQ.data?.items.find((r) => r.fqn === selectedRepoFqn) ??\n null)\n : (repoQ.data?.items[0] ?? null),\n [repoQ.data, reposQ.data, selectedRepoFqn]\n );\n\n const repoStats = useMemo(\n () =>\n composeRepoStats({\n repo: activeRepo,\n pulls: pullsQ.data,\n issues: issuesQ.data,\n languages: languagesQ.data,\n contributors: contributorsQ.data,\n securityAlerts: securityQ.data,\n }),\n [activeRepo, pullsQ.data, issuesQ.data, languagesQ.data, contributorsQ.data, securityQ.data]\n );\n\n // CI Status composite.\n const runsQ = useGitopsRuns(ctx, selectedProvider, selectedRepoFqn, { limit: 30 });\n const ciAnalyticsQ = useGitopsPipelineAnalytics(ctx, selectedProvider, selectedRepoFqn);\n const ciStatus = useMemo(\n () =>\n composeCiStatus({\n pipelines: undefined,\n runs: runsQ.data,\n analytics: ciAnalyticsQ.data,\n }),\n [runsQ.data, ciAnalyticsQ.data]\n );\n\n const repoStatsLoading = pullsQ.isLoading || issuesQ.isLoading || languagesQ.isLoading;\n const ciStatusLoading = runsQ.isLoading || ciAnalyticsQ.isLoading;\n const repoStatsError = (pullsQ.error || issuesQ.error || languagesQ.error)?.message;\n const ciStatusError = (runsQ.error || ciAnalyticsQ.error)?.message;\n\n const handleRefresh = useCallback(() => {\n void qc.invalidateQueries({ queryKey: ['gitops', workspaceId ?? '', agentId, profile] });\n }, [qc, workspaceId, agentId, profile]);\n\n const isRefreshing = pullsQ.isFetching || runsQ.isFetching;\n\n const subTabs: { id: GitOpsSubTab; label: string; icon: typeof BarChart3 }[] = [\n { id: 'repo-stats', label: tr('gitops.repoStats', 'Repo Stats'), icon: BarChart3 },\n { id: 'ci', label: tr('gitops.ci', 'CI Pipelines'), icon: Play },\n { id: 'git-ui', label: tr('gitops.gitUI', 'Git UI'), icon: GitBranch },\n ];\n\n // Ungit iframe state (unchanged from v1).\n const [ungitIframeUrl, setUngitIframeUrl] = useState<string | null>(null);\n const [ungitTokenError, setUngitTokenError] = useState<string | null>(null);\n useEffect(() => {\n let cancelled = false;\n let handle: IframeTokenAutoRefreshHandle | null = null;\n const setup = async () => {\n if (!agentId || !tunnelUrl || subTab !== 'git-ui') {\n setUngitIframeUrl(null);\n return;\n }\n try {\n // F5 residual — Ungit's reverse proxy lives at `/ungit/` (declared\n // by the external `vibe-plugin-tool-git` package's publicPaths +\n // meta.url override). The plugin's createUngitProxy validates the\n // entry-document auth via `?vt=<token>` (or `?apiKey=`), then\n // issues its own session cookie for sub-requests. Switching this\n // to a host-level hash→cookie shim (like F5 does for `/ui/<p>`)\n // would require an SDK contract change + coordinated republish of\n // the external plugin package. Until that ships, the iframe-token\n // sits in the URL once. Mitigations in place: F6 makes it single-\n // use (replayed token rejected); F7 strips the token before\n // upstream forwarding so Ungit never sees it; F10 enforces Origin\n // on non-GET/HEAD sub-requests. Residual surface: one query string\n // appears in CF logs + browser history for the initial document.\n const initial: IframeTokenResponse = await mintIframeToken(agentId, profile, '/ungit');\n if (cancelled) return;\n const baseUrl = `${tunnelUrl.replace(/\\/$/, '')}/ungit/`;\n setUngitIframeUrl(appendIframeTokenQuery(baseUrl, initial.token));\n handle = scheduleIframeTokenRefresh(\n agentId,\n profile,\n '/ungit',\n initial,\n (next: IframeTokenResponse) => {\n setUngitIframeUrl(appendIframeTokenQuery(baseUrl, next.token));\n }\n );\n } catch (err) {\n if (!cancelled) {\n setUngitTokenError(err instanceof Error ? err.message : 'Failed to fetch iframe token');\n }\n }\n };\n void setup();\n return () => {\n cancelled = true;\n handle?.cancel();\n };\n }, [agentId, profile, tunnelUrl, subTab]);\n\n return (\n <motion.div\n key=\"gitops\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n data-vibe-id={vibeId}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <GitMerge className=\"size-5 text-text-secondary\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibeDetails.gitopsTitle', 'GitOps')}\n </h3>\n </div>\n <div className=\"flex items-center gap-2\">\n {hasProviders && (\n <GitOpsProviderPicker\n providers={authedProviders.map((p) => p.name as ProviderName)}\n selected={selectedProvider}\n onChange={setSelectedProvider}\n />\n )}\n {hasProviders && (subTab === 'repo-stats' || subTab === 'ci') && (\n <button\n type=\"button\"\n onClick={handleRefresh}\n disabled={isRefreshing}\n className=\"flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-md border border-border-default text-text-secondary hover:text-text-primary hover:border-border-strong disabled:opacity-50 transition-colors\"\n data-testid=\"gitops-refresh\"\n >\n <RefreshCw className={`size-3 ${isRefreshing ? 'animate-spin' : ''}`} />\n {tr('gitops.refresh', 'Refresh')}\n </button>\n )}\n {!showSetup && (\n <button\n type=\"button\"\n onClick={() => setShowSetup(true)}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n data-testid=\"gitops-add-provider\"\n >\n {hasProviders\n ? tr('gitops.addAnotherProvider', 'Add Provider')\n : tr('gitops.addProvider', 'Add Provider')}\n </button>\n )}\n </div>\n </div>\n\n <div className=\"flex flex-col gap-4 lg:flex-row lg:gap-6\">\n <nav\n className=\"w-full flex-shrink-0 rounded-lg border border-border-default bg-bg-surface p-2 lg:w-56\"\n role=\"tablist\"\n aria-label={tr('gitops.navigation', 'GitOps navigation')}\n >\n {subTabs.map((tab) => {\n const Icon = tab.icon;\n const isActive = subTab === tab.id;\n return (\n <button\n type=\"button\"\n key={tab.id}\n role=\"tab\"\n aria-selected={isActive}\n onClick={() => setSubTab(tab.id)}\n className={`flex w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm font-medium transition-colors ${\n isActive\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-secondary hover:bg-action-ghost-bg-hover hover:text-text-primary'\n }`}\n >\n <Icon className=\"size-4 shrink-0\" />\n <span className=\"truncate\">{tab.label}</span>\n </button>\n );\n })}\n </nav>\n\n <div className=\"min-w-0 flex-1 space-y-4\">\n {showSetup && (\n <GitOpsSetup\n ctx={ctx}\n onCreated={() => {\n setShowSetup(false);\n void providersQ.refetch();\n }}\n onCancel={() => setShowSetup(false)}\n />\n )}\n\n {subTab === 'repo-stats' && !showSetup && (\n <>\n {!hasProviders ? (\n <EmptyState\n tr={tr}\n icon={GitMerge}\n message=\"gitops.noProvidersVibe\"\n fallback=\"No GitOps provider configured. Add a PAT to see repository stats.\"\n ctaLabel={tr('gitops.addProvider', 'Add Provider')}\n onCta={() => setShowSetup(true)}\n />\n ) : !agentId ? (\n <Notice\n tr={tr}\n message=\"gitops.noTunnel\"\n fallback=\"Waiting for agent tunnel. Make sure the agent is running and a tunnel is active.\"\n />\n ) : (\n <RepoStatsTab\n stats={repoStats}\n loading={repoStatsLoading || providersQ.isLoading}\n error={repoStatsError}\n />\n )}\n </>\n )}\n\n {subTab === 'ci' && !showSetup && (\n <>\n {!hasProviders ? (\n <EmptyState\n tr={tr}\n icon={Play}\n message=\"gitops.noProvidersCi\"\n fallback=\"No GitOps provider configured. Add a PAT to see CI pipeline status.\"\n ctaLabel={tr('gitops.addProvider', 'Add Provider')}\n onCta={() => setShowSetup(true)}\n />\n ) : !agentId ? (\n <Notice tr={tr} message=\"gitops.noTunnelCi\" fallback=\"Waiting for agent tunnel.\" />\n ) : (\n <CITab\n status={ciStatus}\n loading={ciStatusLoading || providersQ.isLoading}\n error={ciStatusError}\n />\n )}\n </>\n )}\n\n {subTab === 'git-ui' && (\n <PluginGate\n agentId={agentId || null}\n required={[GIT_PACKAGE]}\n mode=\"all\"\n eyebrow={tr('gitops.ungitEyebrow', 'Ungit plugin required')}\n title={tr('gitops.ungitTitle', 'Install the visual git client')}\n description={tr(\n 'gitops.ungitDescription',\n 'The Git UI embeds the Ungit visual git client served by your agent. Install the plugin to continue.'\n )}\n >\n {ungitTokenError ? (\n <div className=\"rounded-lg border border-border-default bg-status-error-bg-subtle p-4\">\n <p className=\"text-sm text-status-error-text\">{ungitTokenError}</p>\n </div>\n ) : ungitIframeUrl ? (\n <iframe\n src={ungitIframeUrl}\n title=\"Ungit\"\n className=\"w-full min-h-[600px] rounded-lg border border-border-default\"\n />\n ) : (\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr('gitops.ungitLoading', 'Loading Ungit…')}\n </p>\n </div>\n )}\n </PluginGate>\n )}\n </div>\n </div>\n </motion.div>\n );\n};\n\ninterface EmptyStateProps {\n tr: (key: string, fallback: string) => string;\n icon: typeof BarChart3;\n message: string;\n fallback: string;\n ctaLabel?: string;\n onCta?: () => void;\n}\n\nconst EmptyState: FC<EmptyStateProps> = ({\n tr,\n icon: Icon,\n message,\n fallback,\n ctaLabel,\n onCta,\n}) => (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-6 text-center\">\n <Icon className=\"size-8 text-text-secondary mx-auto mb-3\" />\n <p className=\"text-sm text-text-secondary\">{tr(message, fallback)}</p>\n {ctaLabel && onCta && (\n <button\n type=\"button\"\n onClick={onCta}\n className=\"mt-3 px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {ctaLabel}\n </button>\n )}\n </div>\n);\n\ninterface NoticeProps {\n tr: (key: string, fallback: string) => string;\n message: string;\n fallback: string;\n}\n\nconst Notice: FC<NoticeProps> = ({ tr, message, fallback }) => (\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4\">\n <p className=\"text-sm text-text-secondary\">{tr(message, fallback)}</p>\n </div>\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VibeGitOpsTab.js","names":[],"sources":["../../../src/components/gitops/VibeGitOpsTab.tsx"],"sourcesContent":["/**\n * VibeGitOpsTab — GitOps tab content for the Vibe detail page.\n *\n * V2: All data is sourced from the agent over the tunnel via\n * `/api/profiles/<profile>/gitops/<provider>/*`. No backend GraphQL\n * involvement. The backend's old `gitopsProvider` records are deprecated.\n *\n * Sub-tabs:\n * 1. Repo Stats — repo metadata + PR + issue + security counts\n * 2. CI Pipelines — recent runs + analytics percentiles\n * 3. Git UI — Ungit iframe (when vibe-plugin-tool-git is installed)\n */\nimport { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport { motion } from 'framer-motion';\nimport { GitMerge, BarChart3, Play, GitBranch, RefreshCw } from 'lucide-react';\nimport { useQueryClient } from '@tanstack/react-query';\n\nimport { RepoStatsTab } from './RepoStatsTab';\nimport { CITab } from './CITab';\nimport { GitOpsSetup } from './GitOpsSetup';\nimport { GitOpsProviderPicker } from './GitOpsProviderPicker';\nimport { PluginGate } from '@/components/shared';\nimport { GIT_PACKAGE } from '@/constants/pluginCatalog';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\nimport {\n useGitopsContributors,\n useGitopsIssues,\n useGitopsLanguages,\n useGitopsPipelineAnalytics,\n useGitopsProviders,\n useGitopsPullRequests,\n useGitopsRepos,\n useGitopsRuns,\n useGitopsSecurityAlerts,\n} from '@/hooks/useGitops';\nimport { composeCiStatus, composeRepoStats, type ProviderName, type RepoFqn } from './types';\nimport {\n mintIframeToken,\n scheduleIframeTokenRefresh,\n appendIframeTokenQuery,\n type IframeTokenAutoRefreshHandle,\n type IframeTokenResponse,\n} from '@/services/iframeToken';\n\ninterface VibeGitOpsTabProps {\n vibeId: string;\n agentId: string;\n /** Canonical agent profile name. */\n profile: string;\n /**\n * Tunnel URL — retained ONLY for the `git-ui` iframe handshake\n * (Ungit reverse-proxied at `/git/`) which still bootstraps via a\n * one-time iframe token. GitOps REST calls no longer use this; they\n * route through the svc proxy (F4 Wave C).\n */\n tunnelUrl: string | null;\n tr: (key: string, fallback: string) => string;\n}\n\ntype GitOpsSubTab = 'repo-stats' | 'ci' | 'git-ui';\n\nexport const VibeGitOpsTab: FC<VibeGitOpsTabProps> = ({\n vibeId,\n agentId,\n profile,\n tunnelUrl,\n tr,\n}) => {\n const [subTab, setSubTab] = useState<GitOpsSubTab>('repo-stats');\n const [showSetup, setShowSetup] = useState(false);\n const [selectedProvider, setSelectedProvider] = useState<ProviderName | null>(null);\n const [selectedRepoFqn, setSelectedRepoFqn] = useState<RepoFqn | null>(null);\n const { workspaceId } = useVibeControls();\n const qc = useQueryClient();\n\n const ctx = useMemo(\n () => ({ profile, workspaceId: workspaceId ?? '', agentId }),\n [profile, workspaceId, agentId]\n );\n\n // Providers registered on the agent.\n const providersQ = useGitopsProviders(ctx);\n const authedProviders = useMemo(\n () => providersQ.data?.filter((p) => p.registered && p.authenticated) ?? [],\n [providersQ.data]\n );\n const hasProviders = authedProviders.length > 0;\n\n // Pick a default once data loads.\n useEffect(() => {\n if (!selectedProvider && authedProviders.length > 0) {\n const first = authedProviders[0];\n if (first) {\n setSelectedProvider(first.name as ProviderName);\n }\n }\n }, [authedProviders, selectedProvider]);\n\n // Once a provider is selected, fetch repos for it and pick the first one\n // by default. The user can switch repos via the picker.\n const reposQ = useGitopsRepos(ctx, selectedProvider, { limit: 30 });\n useEffect(() => {\n if (!selectedRepoFqn && reposQ.data?.items.length) {\n const first = reposQ.data.items[0];\n if (first) {\n setSelectedRepoFqn(first.fqn);\n }\n }\n }, [reposQ.data, selectedRepoFqn]);\n\n // Repo Stats composite — runs only when active sub-tab is repo-stats.\n const repoQ = useGitopsRepos(ctx, selectedProvider, { limit: 1 }); // metadata via list (1st item)\n const repoDetailQ = useGitopsRepos.length ? null : null; // keep TS happy; we use repoQ.items[0]\n const pullsQ = useGitopsPullRequests(ctx, selectedProvider, selectedRepoFqn, {\n state: 'open',\n limit: 100,\n });\n const issuesQ = useGitopsIssues(ctx, selectedProvider, selectedRepoFqn, {\n state: 'open',\n limit: 100,\n });\n const languagesQ = useGitopsLanguages(ctx, selectedProvider, selectedRepoFqn);\n const contributorsQ = useGitopsContributors(ctx, selectedProvider, selectedRepoFqn, 20);\n const securityQ = useGitopsSecurityAlerts(ctx, selectedProvider, selectedRepoFqn);\n\n const activeRepo = useMemo(\n () =>\n selectedRepoFqn\n ? (repoQ.data?.items.find((r) => r.fqn === selectedRepoFqn) ??\n reposQ.data?.items.find((r) => r.fqn === selectedRepoFqn) ??\n null)\n : (repoQ.data?.items[0] ?? null),\n [repoQ.data, reposQ.data, selectedRepoFqn]\n );\n\n const repoStats = useMemo(\n () =>\n composeRepoStats({\n repo: activeRepo,\n pulls: pullsQ.data,\n issues: issuesQ.data,\n languages: languagesQ.data,\n contributors: contributorsQ.data,\n securityAlerts: securityQ.data,\n }),\n [activeRepo, pullsQ.data, issuesQ.data, languagesQ.data, contributorsQ.data, securityQ.data]\n );\n\n // CI Status composite.\n const runsQ = useGitopsRuns(ctx, selectedProvider, selectedRepoFqn, { limit: 30 });\n const ciAnalyticsQ = useGitopsPipelineAnalytics(ctx, selectedProvider, selectedRepoFqn);\n const ciStatus = useMemo(\n () =>\n composeCiStatus({\n pipelines: undefined,\n runs: runsQ.data,\n analytics: ciAnalyticsQ.data,\n }),\n [runsQ.data, ciAnalyticsQ.data]\n );\n\n const repoStatsLoading = pullsQ.isLoading || issuesQ.isLoading || languagesQ.isLoading;\n const ciStatusLoading = runsQ.isLoading || ciAnalyticsQ.isLoading;\n const repoStatsError = (pullsQ.error || issuesQ.error || languagesQ.error)?.message;\n const ciStatusError = (runsQ.error || ciAnalyticsQ.error)?.message;\n\n const handleRefresh = useCallback(() => {\n void qc.invalidateQueries({ queryKey: ['gitops', workspaceId ?? '', agentId, profile] });\n }, [qc, workspaceId, agentId, profile]);\n\n const isRefreshing = pullsQ.isFetching || runsQ.isFetching;\n\n const subTabs: { id: GitOpsSubTab; label: string; icon: typeof BarChart3 }[] = [\n { id: 'repo-stats', label: tr('gitops.repoStats', 'Repo Stats'), icon: BarChart3 },\n { id: 'ci', label: tr('gitops.ci', 'CI Pipelines'), icon: Play },\n { id: 'git-ui', label: tr('gitops.gitUI', 'Git UI'), icon: GitBranch },\n ];\n\n // Ungit iframe state (unchanged from v1).\n const [ungitIframeUrl, setUngitIframeUrl] = useState<string | null>(null);\n const [ungitTokenError, setUngitTokenError] = useState<string | null>(null);\n useEffect(() => {\n let cancelled = false;\n let handle: IframeTokenAutoRefreshHandle | null = null;\n const setup = async () => {\n if (!agentId || !tunnelUrl || subTab !== 'git-ui') {\n setUngitIframeUrl(null);\n return;\n }\n try {\n // audit-B P0-SEC-03 residual closure — Ungit's reverse proxy\n // now lives at `/git/` (was `/ungit/`). `/git` joins\n // `/terminal`, `/ui`, `/plan`, `/code-server` as a first-class\n // iframe-token surface in the agent's `isValidIframePathPrefix`\n // allow-list, so the microfe can mint a scoped single-use\n // token and pass it once as `?vt=` for the entry document. The\n // plugin proxy verifies the ticket via `iframeBridge`, issues\n // its own `__vibe_ungit_session` cookie, and strips the token\n // from upstream forwards. Sub-requests use the cookie + Origin\n // CSRF guard. Mitigations: F6 single-use; F7 upstream strip;\n // F10 Origin on non-GET/HEAD.\n const initial: IframeTokenResponse = await mintIframeToken(agentId, profile, '/git');\n if (cancelled) return;\n const baseUrl = `${tunnelUrl.replace(/\\/$/, '')}/git/`;\n setUngitIframeUrl(appendIframeTokenQuery(baseUrl, initial.token));\n handle = scheduleIframeTokenRefresh(\n agentId,\n profile,\n '/git',\n initial,\n (next: IframeTokenResponse) => {\n setUngitIframeUrl(appendIframeTokenQuery(baseUrl, next.token));\n }\n );\n } catch (err) {\n if (!cancelled) {\n setUngitTokenError(err instanceof Error ? err.message : 'Failed to fetch iframe token');\n }\n }\n };\n void setup();\n return () => {\n cancelled = true;\n handle?.cancel();\n };\n }, [agentId, profile, tunnelUrl, subTab]);\n\n return (\n <motion.div\n key=\"gitops\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -8 }}\n transition={{ duration: 0.2 }}\n className=\"space-y-4\"\n data-vibe-id={vibeId}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <GitMerge className=\"size-5 text-text-secondary\" />\n <h3 className=\"font-semibold text-text-primary\">\n {tr('vibeDetails.gitopsTitle', 'GitOps')}\n </h3>\n </div>\n <div className=\"flex items-center gap-2\">\n {hasProviders && (\n <GitOpsProviderPicker\n providers={authedProviders.map((p) => p.name as ProviderName)}\n selected={selectedProvider}\n onChange={setSelectedProvider}\n />\n )}\n {hasProviders && (subTab === 'repo-stats' || subTab === 'ci') && (\n <button\n type=\"button\"\n onClick={handleRefresh}\n disabled={isRefreshing}\n className=\"flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-md border border-border-default text-text-secondary hover:text-text-primary hover:border-border-strong disabled:opacity-50 transition-colors\"\n data-testid=\"gitops-refresh\"\n >\n <RefreshCw className={`size-3 ${isRefreshing ? 'animate-spin' : ''}`} />\n {tr('gitops.refresh', 'Refresh')}\n </button>\n )}\n {!showSetup && (\n <button\n type=\"button\"\n onClick={() => setShowSetup(true)}\n className=\"flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n data-testid=\"gitops-add-provider\"\n >\n {hasProviders\n ? tr('gitops.addAnotherProvider', 'Add Provider')\n : tr('gitops.addProvider', 'Add Provider')}\n </button>\n )}\n </div>\n </div>\n\n <div className=\"flex flex-col gap-4 lg:flex-row lg:gap-6\">\n <nav\n className=\"w-full flex-shrink-0 rounded-lg border border-border-default bg-bg-surface p-2 lg:w-56\"\n role=\"tablist\"\n aria-label={tr('gitops.navigation', 'GitOps navigation')}\n >\n {subTabs.map((tab) => {\n const Icon = tab.icon;\n const isActive = subTab === tab.id;\n return (\n <button\n type=\"button\"\n key={tab.id}\n role=\"tab\"\n aria-selected={isActive}\n onClick={() => setSubTab(tab.id)}\n className={`flex w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm font-medium transition-colors ${\n isActive\n ? 'bg-action-primary-bg text-action-primary-text shadow-sm'\n : 'text-text-secondary hover:bg-action-ghost-bg-hover hover:text-text-primary'\n }`}\n >\n <Icon className=\"size-4 shrink-0\" />\n <span className=\"truncate\">{tab.label}</span>\n </button>\n );\n })}\n </nav>\n\n <div className=\"min-w-0 flex-1 space-y-4\">\n {showSetup && (\n <GitOpsSetup\n ctx={ctx}\n onCreated={() => {\n setShowSetup(false);\n void providersQ.refetch();\n }}\n onCancel={() => setShowSetup(false)}\n />\n )}\n\n {subTab === 'repo-stats' && !showSetup && (\n <>\n {!hasProviders ? (\n <EmptyState\n tr={tr}\n icon={GitMerge}\n message=\"gitops.noProvidersVibe\"\n fallback=\"No GitOps provider configured. Add a PAT to see repository stats.\"\n ctaLabel={tr('gitops.addProvider', 'Add Provider')}\n onCta={() => setShowSetup(true)}\n />\n ) : !agentId ? (\n <Notice\n tr={tr}\n message=\"gitops.noTunnel\"\n fallback=\"Waiting for agent tunnel. Make sure the agent is running and a tunnel is active.\"\n />\n ) : (\n <RepoStatsTab\n stats={repoStats}\n loading={repoStatsLoading || providersQ.isLoading}\n error={repoStatsError}\n />\n )}\n </>\n )}\n\n {subTab === 'ci' && !showSetup && (\n <>\n {!hasProviders ? (\n <EmptyState\n tr={tr}\n icon={Play}\n message=\"gitops.noProvidersCi\"\n fallback=\"No GitOps provider configured. Add a PAT to see CI pipeline status.\"\n ctaLabel={tr('gitops.addProvider', 'Add Provider')}\n onCta={() => setShowSetup(true)}\n />\n ) : !agentId ? (\n <Notice tr={tr} message=\"gitops.noTunnelCi\" fallback=\"Waiting for agent tunnel.\" />\n ) : (\n <CITab\n status={ciStatus}\n loading={ciStatusLoading || providersQ.isLoading}\n error={ciStatusError}\n />\n )}\n </>\n )}\n\n {subTab === 'git-ui' && (\n <PluginGate\n agentId={agentId || null}\n required={[GIT_PACKAGE]}\n mode=\"all\"\n eyebrow={tr('gitops.ungitEyebrow', 'Ungit plugin required')}\n title={tr('gitops.ungitTitle', 'Install the visual git client')}\n description={tr(\n 'gitops.ungitDescription',\n 'The Git UI embeds the Ungit visual git client served by your agent. Install the plugin to continue.'\n )}\n >\n {ungitTokenError ? (\n <div className=\"rounded-lg border border-border-default bg-status-error-bg-subtle p-4\">\n <p className=\"text-sm text-status-error-text\">{ungitTokenError}</p>\n </div>\n ) : ungitIframeUrl ? (\n <iframe\n src={ungitIframeUrl}\n title=\"Ungit\"\n className=\"w-full min-h-[600px] rounded-lg border border-border-default\"\n />\n ) : (\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr('gitops.ungitLoading', 'Loading Ungit…')}\n </p>\n </div>\n )}\n </PluginGate>\n )}\n </div>\n </div>\n </motion.div>\n );\n};\n\ninterface EmptyStateProps {\n tr: (key: string, fallback: string) => string;\n icon: typeof BarChart3;\n message: string;\n fallback: string;\n ctaLabel?: string;\n onCta?: () => void;\n}\n\nconst EmptyState: FC<EmptyStateProps> = ({\n tr,\n icon: Icon,\n message,\n fallback,\n ctaLabel,\n onCta,\n}) => (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-6 text-center\">\n <Icon className=\"size-8 text-text-secondary mx-auto mb-3\" />\n <p className=\"text-sm text-text-secondary\">{tr(message, fallback)}</p>\n {ctaLabel && onCta && (\n <button\n type=\"button\"\n onClick={onCta}\n className=\"mt-3 px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n {ctaLabel}\n </button>\n )}\n </div>\n);\n\ninterface NoticeProps {\n tr: (key: string, fallback: string) => string;\n message: string;\n fallback: string;\n}\n\nconst Notice: FC<NoticeProps> = ({ tr, message, fallback }) => (\n <div className=\"rounded-lg border border-border-default bg-bg-sunken p-4\">\n <p className=\"text-sm text-text-secondary\">{tr(message, fallback)}</p>\n </div>\n);\n"],"mappings":"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"}
|
|
@@ -1,39 +1,40 @@
|
|
|
1
1
|
import { useCreateSessionShareLinkMutation as e, useGetSessionShareLinksQuery as t, useGetSessionSharesQuery as n, useRevokeSessionShareLinkMutation as r, useRevokeSessionShareMutation as i, useShareSessionMutation as a } from "../../generated/wspace-operations.js";
|
|
2
|
-
import { useTr as
|
|
3
|
-
import { SessionSharePermission as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { useTr as ee } from "../../shared/hooks/useTr.js";
|
|
3
|
+
import { SessionSharePermission as o, ShareStatus as s } from "../../generated/wspace-types.js";
|
|
4
|
+
import { copyTextToClipboard as te, normalizeShareUrl as c } from "../../utils/shareUrl.js";
|
|
5
|
+
import { useEffect as ne, useRef as re, useState as l } from "react";
|
|
6
|
+
import { Check as ie, Clock as u, Copy as d, Eye as f, Gamepad2 as p, Link2 as m, Lock as ae, RefreshCw as h, Share2 as oe, Trash2 as g, Users as _, X as v } from "lucide-react";
|
|
6
7
|
import { Fragment as y, jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
7
8
|
//#region src/components/sessions/SessionShareDialog.tsx
|
|
8
9
|
var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
9
|
-
let T =
|
|
10
|
+
let T = ee(), E = re(null), [D, O] = l("users"), [se, k] = l(null), [ce, A] = l(null), [j, M] = l(""), [N, le] = l(o.View), [P, F] = l(""), [I, ue] = l(24), [L, R] = l(""), [z, B] = l(o.View), [V, H] = l(1), [U, W] = l(""), [G, K] = l(null), { data: de, refetch: q } = n({
|
|
10
11
|
variables: { sessionId: S.id },
|
|
11
12
|
skip: !C
|
|
12
|
-
}), { data:
|
|
13
|
+
}), { data: fe, refetch: J } = t({
|
|
13
14
|
variables: { sessionId: S.id },
|
|
14
15
|
skip: !C
|
|
15
|
-
}), [
|
|
16
|
-
|
|
16
|
+
}), [pe, { loading: Y }] = a(), [me, { loading: X }] = e(), [he, { loading: ge }] = i(), [_e, { loading: ve }] = r(), Z = de?.sessionShares?.filter((e) => e.status === s.Active) || [], Q = fe?.sessionShareLinks?.filter((e) => e.status === s.Active) || [];
|
|
17
|
+
ne(() => {
|
|
17
18
|
let e = E.current;
|
|
18
19
|
e && (C ? e.showModal() : e.close());
|
|
19
20
|
}, [C]);
|
|
20
|
-
let
|
|
21
|
-
if (e.preventDefault(),
|
|
21
|
+
let ye = async (e) => {
|
|
22
|
+
if (e.preventDefault(), j.trim()) try {
|
|
22
23
|
let e = new Date(Date.now() + I * 60 * 60 * 1e3).toISOString();
|
|
23
|
-
await
|
|
24
|
+
await pe({ variables: { input: {
|
|
24
25
|
sessionId: S.id,
|
|
25
|
-
sharedWithEmail:
|
|
26
|
-
permission:
|
|
26
|
+
sharedWithEmail: j.trim(),
|
|
27
|
+
permission: N,
|
|
27
28
|
expiresAt: e,
|
|
28
29
|
message: P.trim() || void 0
|
|
29
|
-
} } }),
|
|
30
|
+
} } }), M(""), F(""), q();
|
|
30
31
|
} catch (e) {
|
|
31
32
|
console.error("Failed to share session:", e);
|
|
32
33
|
}
|
|
33
|
-
},
|
|
34
|
+
}, be = async (e) => {
|
|
34
35
|
e.preventDefault();
|
|
35
36
|
try {
|
|
36
|
-
await
|
|
37
|
+
await me({ variables: { input: {
|
|
37
38
|
sessionId: S.id,
|
|
38
39
|
name: L.trim() || void 0,
|
|
39
40
|
permission: z,
|
|
@@ -44,26 +45,23 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
44
45
|
} catch (e) {
|
|
45
46
|
console.error("Failed to create share link:", e);
|
|
46
47
|
}
|
|
47
|
-
},
|
|
48
|
+
}, xe = async (e) => {
|
|
48
49
|
try {
|
|
49
|
-
await
|
|
50
|
+
await he({ variables: { id: e } }), q();
|
|
50
51
|
} catch (e) {
|
|
51
52
|
console.error("Failed to revoke share:", e);
|
|
52
53
|
}
|
|
53
|
-
},
|
|
54
|
+
}, Se = async (e) => {
|
|
54
55
|
try {
|
|
55
|
-
await
|
|
56
|
+
await _e({ variables: { id: e } }), J();
|
|
56
57
|
} catch (e) {
|
|
57
58
|
console.error("Failed to revoke link:", e);
|
|
58
59
|
}
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
t.value = e, t.style.position = "fixed", t.style.opacity = "0", document.body.appendChild(t), t.select(), document.execCommand("copy"), document.body.removeChild(t);
|
|
65
|
-
}
|
|
66
|
-
k(!0), setTimeout(() => k(!1), 2e3);
|
|
60
|
+
}, Ce = async (e, t) => {
|
|
61
|
+
let n = await te(c(t)).then(() => !0, (e) => (console.warn("Failed to copy session share link:", e), !1));
|
|
62
|
+
k(n ? e : null), A(n ? null : e), setTimeout(() => {
|
|
63
|
+
k((t) => t === e ? null : t), A((t) => t === e ? null : t);
|
|
64
|
+
}, 2e3);
|
|
67
65
|
}, $ = (e) => {
|
|
68
66
|
let t = new Date(e), n = /* @__PURE__ */ new Date(), r = t.getTime() - n.getTime(), i = Math.floor(r / (1e3 * 60 * 60)), a = Math.floor(r % (1e3 * 60 * 60) / (1e3 * 60));
|
|
69
67
|
return r < 0 ? "Expired" : i > 24 ? `${Math.floor(i / 24)}d` : i > 0 ? `${i}h ${a}m` : `${a}m`;
|
|
@@ -81,7 +79,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
81
79
|
className: "flex items-center gap-3",
|
|
82
80
|
children: [/* @__PURE__ */ b("div", {
|
|
83
81
|
className: "p-2 bg-action-primary-bg/10 rounded-lg",
|
|
84
|
-
children: /* @__PURE__ */ b(
|
|
82
|
+
children: /* @__PURE__ */ b(oe, { className: "size-5 text-action-primary-bg" })
|
|
85
83
|
}), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("h2", {
|
|
86
84
|
className: "text-lg font-semibold text-text-primary",
|
|
87
85
|
children: T("vibecontrols.sessions.share.title", "Share Session")
|
|
@@ -93,7 +91,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
93
91
|
type: "button",
|
|
94
92
|
onClick: w,
|
|
95
93
|
className: "p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
96
|
-
children: /* @__PURE__ */ b(
|
|
94
|
+
children: /* @__PURE__ */ b(v, { className: "size-5 text-text-secondary" })
|
|
97
95
|
})]
|
|
98
96
|
}),
|
|
99
97
|
/* @__PURE__ */ x("div", {
|
|
@@ -103,7 +101,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
103
101
|
onClick: () => O("users"),
|
|
104
102
|
className: `flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors ${D === "users" ? "text-action-primary-bg border-b-2 border-action-primary-bg" : "text-text-secondary hover:text-text-primary"}`,
|
|
105
103
|
children: [
|
|
106
|
-
/* @__PURE__ */ b(
|
|
104
|
+
/* @__PURE__ */ b(_, { className: "size-4" }),
|
|
107
105
|
T("vibecontrols.sessions.share.shareWithUser", "Share with User"),
|
|
108
106
|
Z.length > 0 && /* @__PURE__ */ b("span", {
|
|
109
107
|
className: "ml-1 px-1.5 py-0.5 text-xs bg-bg-sunken rounded-full",
|
|
@@ -115,7 +113,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
115
113
|
onClick: () => O("links"),
|
|
116
114
|
className: `flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors ${D === "links" ? "text-action-primary-bg border-b-2 border-action-primary-bg" : "text-text-secondary hover:text-text-primary"}`,
|
|
117
115
|
children: [
|
|
118
|
-
/* @__PURE__ */ b(
|
|
116
|
+
/* @__PURE__ */ b(m, { className: "size-4" }),
|
|
119
117
|
T("vibecontrols.sessions.share.createLink", "Create Link"),
|
|
120
118
|
Q.length > 0 && /* @__PURE__ */ b("span", {
|
|
121
119
|
className: "ml-1 px-1.5 py-0.5 text-xs bg-bg-sunken rounded-full",
|
|
@@ -129,7 +127,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
129
127
|
children: D === "users" ? /* @__PURE__ */ x("div", {
|
|
130
128
|
className: "space-y-6",
|
|
131
129
|
children: [/* @__PURE__ */ x("form", {
|
|
132
|
-
onSubmit:
|
|
130
|
+
onSubmit: ye,
|
|
133
131
|
className: "space-y-4",
|
|
134
132
|
children: [
|
|
135
133
|
/* @__PURE__ */ x("div", { children: [
|
|
@@ -140,8 +138,8 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
140
138
|
/* @__PURE__ */ b("input", {
|
|
141
139
|
"aria-label": "teammate@company.com",
|
|
142
140
|
type: "email",
|
|
143
|
-
value:
|
|
144
|
-
onChange: (e) =>
|
|
141
|
+
value: j,
|
|
142
|
+
onChange: (e) => M(e.target.value),
|
|
145
143
|
placeholder: "teammate@company.com",
|
|
146
144
|
required: !0,
|
|
147
145
|
className: "w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg"
|
|
@@ -157,14 +155,14 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
157
155
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
158
156
|
children: T("vibecontrols.sessions.share.permission", "Permission")
|
|
159
157
|
}), /* @__PURE__ */ x("select", {
|
|
160
|
-
value:
|
|
161
|
-
onChange: (e) =>
|
|
158
|
+
value: N,
|
|
159
|
+
onChange: (e) => le(e.target.value),
|
|
162
160
|
className: "w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
|
|
163
161
|
children: [/* @__PURE__ */ b("option", {
|
|
164
|
-
value:
|
|
162
|
+
value: o.View,
|
|
165
163
|
children: T("vibecontrols.sessions.share.viewOnly", "View Only")
|
|
166
164
|
}), /* @__PURE__ */ b("option", {
|
|
167
|
-
value:
|
|
165
|
+
value: o.Interact,
|
|
168
166
|
children: T("vibecontrols.sessions.share.canControl", "Can Control")
|
|
169
167
|
})]
|
|
170
168
|
})] }), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("label", {
|
|
@@ -172,7 +170,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
172
170
|
children: T("vibecontrols.sessions.share.expiresIn", "Expires In")
|
|
173
171
|
}), /* @__PURE__ */ x("select", {
|
|
174
172
|
value: I,
|
|
175
|
-
onChange: (e) =>
|
|
173
|
+
onChange: (e) => ue(Number(e.target.value)),
|
|
176
174
|
className: "w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
|
|
177
175
|
children: [
|
|
178
176
|
/* @__PURE__ */ b("option", {
|
|
@@ -215,17 +213,17 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
215
213
|
})] }),
|
|
216
214
|
/* @__PURE__ */ b("div", {
|
|
217
215
|
className: "flex items-center gap-2 p-3 bg-bg-sunken rounded-lg text-sm",
|
|
218
|
-
children:
|
|
216
|
+
children: N === o.View ? /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b(f, { className: "size-4 text-text-secondary" }), /* @__PURE__ */ b("span", {
|
|
219
217
|
className: "text-text-secondary",
|
|
220
218
|
children: T("vibecontrols.sessions.share.viewOnlyHint", "User can watch the terminal but cannot type")
|
|
221
|
-
})] }) : /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b(
|
|
219
|
+
})] }) : /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b(p, { className: "size-4 text-text-secondary" }), /* @__PURE__ */ b("span", {
|
|
222
220
|
className: "text-text-secondary",
|
|
223
221
|
children: T("vibecontrols.sessions.share.controlHint", "User can request control to type (turn-based)")
|
|
224
222
|
})] })
|
|
225
223
|
}),
|
|
226
224
|
/* @__PURE__ */ b("button", {
|
|
227
225
|
type: "submit",
|
|
228
|
-
disabled: Y || !
|
|
226
|
+
disabled: Y || !j.trim(),
|
|
229
227
|
className: "w-full px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50",
|
|
230
228
|
children: Y ? T("vibecontrols.sessions.share.sharing", "Sharing...") : T("vibecontrols.sessions.share.shareSession", "Share Session")
|
|
231
229
|
})
|
|
@@ -241,7 +239,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
241
239
|
className: "flex items-center gap-3",
|
|
242
240
|
children: [/* @__PURE__ */ b("div", {
|
|
243
241
|
className: "size-8 rounded-full bg-action-primary-bg/20 flex items-center justify-center",
|
|
244
|
-
children: /* @__PURE__ */ b(
|
|
242
|
+
children: /* @__PURE__ */ b(_, { className: "size-4 text-action-primary-bg" })
|
|
245
243
|
}), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ x("p", {
|
|
246
244
|
className: "text-sm font-medium text-text-primary",
|
|
247
245
|
children: [
|
|
@@ -252,29 +250,29 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
252
250
|
}), /* @__PURE__ */ x("div", {
|
|
253
251
|
className: "flex items-center gap-2 text-xs text-text-secondary",
|
|
254
252
|
children: [
|
|
255
|
-
e.permission ===
|
|
253
|
+
e.permission === o.View ? /* @__PURE__ */ b(f, { className: "size-3" }) : /* @__PURE__ */ b(p, { className: "size-3" }),
|
|
256
254
|
/* @__PURE__ */ b("span", { children: e.permission }),
|
|
257
255
|
e.expiresAt && /* @__PURE__ */ x(y, { children: [
|
|
258
256
|
/* @__PURE__ */ b("span", { children: "·" }),
|
|
259
|
-
/* @__PURE__ */ b(
|
|
257
|
+
/* @__PURE__ */ b(u, { className: "size-3" }),
|
|
260
258
|
/* @__PURE__ */ b("span", { children: $(e.expiresAt) })
|
|
261
259
|
] })
|
|
262
260
|
]
|
|
263
261
|
})] })]
|
|
264
262
|
}), /* @__PURE__ */ b("button", {
|
|
265
263
|
type: "button",
|
|
266
|
-
onClick: () =>
|
|
267
|
-
disabled:
|
|
264
|
+
onClick: () => xe(e.id),
|
|
265
|
+
disabled: ge,
|
|
268
266
|
className: "p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg/10 rounded transition-colors",
|
|
269
267
|
title: "Revoke access",
|
|
270
|
-
children: /* @__PURE__ */ b(
|
|
268
|
+
children: /* @__PURE__ */ b(g, { className: "size-4" })
|
|
271
269
|
})]
|
|
272
270
|
}, e.id))
|
|
273
271
|
})] })]
|
|
274
272
|
}) : /* @__PURE__ */ x("div", {
|
|
275
273
|
className: "space-y-6",
|
|
276
274
|
children: [/* @__PURE__ */ x("form", {
|
|
277
|
-
onSubmit:
|
|
275
|
+
onSubmit: be,
|
|
278
276
|
className: "space-y-4",
|
|
279
277
|
children: [
|
|
280
278
|
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("label", {
|
|
@@ -298,10 +296,10 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
298
296
|
onChange: (e) => B(e.target.value),
|
|
299
297
|
className: "w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
|
|
300
298
|
children: [/* @__PURE__ */ b("option", {
|
|
301
|
-
value:
|
|
299
|
+
value: o.View,
|
|
302
300
|
children: T("vibecontrols.sessions.share.viewOnly", "View Only")
|
|
303
301
|
}), /* @__PURE__ */ b("option", {
|
|
304
|
-
value:
|
|
302
|
+
value: o.Interact,
|
|
305
303
|
children: T("vibecontrols.sessions.share.canControl", "Can Control")
|
|
306
304
|
})]
|
|
307
305
|
})] }), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("label", {
|
|
@@ -380,46 +378,46 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
380
378
|
children: [/* @__PURE__ */ x("div", {
|
|
381
379
|
className: "flex items-center gap-2",
|
|
382
380
|
children: [
|
|
383
|
-
/* @__PURE__ */ b(
|
|
381
|
+
/* @__PURE__ */ b(m, { className: "size-4 text-action-primary-bg" }),
|
|
384
382
|
/* @__PURE__ */ b("span", {
|
|
385
383
|
className: "text-sm font-medium text-text-primary",
|
|
386
384
|
children: e.name || "Unnamed Link"
|
|
387
385
|
}),
|
|
388
|
-
e.hasPassword && /* @__PURE__ */ b(
|
|
386
|
+
e.hasPassword && /* @__PURE__ */ b(ae, { className: "size-3 text-text-secondary" })
|
|
389
387
|
]
|
|
390
388
|
}), /* @__PURE__ */ x("div", {
|
|
391
389
|
className: "flex items-center gap-1",
|
|
392
390
|
children: [/* @__PURE__ */ b("button", {
|
|
393
391
|
type: "button",
|
|
394
|
-
onClick: () =>
|
|
392
|
+
onClick: () => Ce(e.id, e.shareUrl),
|
|
395
393
|
className: "p-1.5 text-text-secondary hover:text-action-primary-bg hover:bg-action-primary-bg/10 rounded transition-colors",
|
|
396
394
|
title: "Copy link",
|
|
397
|
-
children:
|
|
395
|
+
children: se === e.id ? /* @__PURE__ */ b(ie, { className: "size-4 text-status-success-text" }) : ce === e.id ? /* @__PURE__ */ b(v, { className: "size-4 text-status-error-text" }) : /* @__PURE__ */ b(d, { className: "size-4" })
|
|
398
396
|
}), /* @__PURE__ */ b("button", {
|
|
399
397
|
type: "button",
|
|
400
|
-
onClick: () =>
|
|
401
|
-
disabled:
|
|
398
|
+
onClick: () => Se(e.id),
|
|
399
|
+
disabled: ve,
|
|
402
400
|
className: "p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg/10 rounded transition-colors",
|
|
403
401
|
title: "Revoke link",
|
|
404
|
-
children: /* @__PURE__ */ b(
|
|
402
|
+
children: /* @__PURE__ */ b(g, { className: "size-4" })
|
|
405
403
|
})]
|
|
406
404
|
})]
|
|
407
405
|
}),
|
|
408
406
|
/* @__PURE__ */ x("div", {
|
|
409
407
|
className: "flex items-center gap-3 text-xs text-text-secondary",
|
|
410
408
|
children: [
|
|
411
|
-
e.permission ===
|
|
409
|
+
e.permission === o.View ? /* @__PURE__ */ x("span", {
|
|
412
410
|
className: "flex items-center gap-1",
|
|
413
|
-
children: [/* @__PURE__ */ b(
|
|
411
|
+
children: [/* @__PURE__ */ b(f, { className: "size-3" }), " View"]
|
|
414
412
|
}) : /* @__PURE__ */ x("span", {
|
|
415
413
|
className: "flex items-center gap-1",
|
|
416
|
-
children: [/* @__PURE__ */ b(
|
|
414
|
+
children: [/* @__PURE__ */ b(p, { className: "size-3" }), " Control"]
|
|
417
415
|
}),
|
|
418
416
|
/* @__PURE__ */ b("span", { children: "·" }),
|
|
419
417
|
/* @__PURE__ */ x("span", {
|
|
420
418
|
className: "flex items-center gap-1",
|
|
421
419
|
children: [
|
|
422
|
-
/* @__PURE__ */ b(
|
|
420
|
+
/* @__PURE__ */ b(u, { className: "size-3" }),
|
|
423
421
|
" ",
|
|
424
422
|
$(e.expiresAt)
|
|
425
423
|
]
|
|
@@ -428,7 +426,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
428
426
|
/* @__PURE__ */ x("span", {
|
|
429
427
|
className: "flex items-center gap-1",
|
|
430
428
|
children: [
|
|
431
|
-
/* @__PURE__ */ b(
|
|
429
|
+
/* @__PURE__ */ b(h, { className: "size-3" }),
|
|
432
430
|
" ",
|
|
433
431
|
e.useCount,
|
|
434
432
|
e.maxUses ? `/${e.maxUses}` : "",
|
|
@@ -442,7 +440,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
442
440
|
children: /* @__PURE__ */ b("input", {
|
|
443
441
|
"aria-label": "control",
|
|
444
442
|
type: "text",
|
|
445
|
-
value: e.shareUrl,
|
|
443
|
+
value: c(e.shareUrl),
|
|
446
444
|
readOnly: !0,
|
|
447
445
|
className: "flex-1 px-2 py-1 text-xs font-mono bg-bg-surface border border-border-default rounded text-text-secondary truncate"
|
|
448
446
|
})
|