diff --git a/ui/src/components/account/accounts-table.tsx b/ui/src/components/account/accounts-table.tsx index d1b7232c..757ff701 100644 --- a/ui/src/components/account/accounts-table.tsx +++ b/ui/src/components/account/accounts-table.tsx @@ -24,12 +24,13 @@ import { AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; -import { Check, Pencil, Trash2, RotateCcw } from 'lucide-react'; +import { Check, CheckCheck, Link2, Pencil, RotateCcw, Trash2, Unlink } from 'lucide-react'; import { EditAccountContextDialog } from '@/components/account/edit-account-context-dialog'; import { useSetDefaultAccount, useDeleteAccount, useResetDefaultAccount, + useUpdateAccountContext, } from '@/hooks/use-accounts'; import type { Account } from '@/lib/api-client'; @@ -42,6 +43,7 @@ export function AccountsTable({ data, defaultAccount }: AccountsTableProps) { const setDefaultMutation = useSetDefaultAccount(); const deleteMutation = useDeleteAccount(); const resetDefaultMutation = useResetDefaultAccount(); + const updateContextMutation = useUpdateAccountContext(); const [deleteTarget, setDeleteTarget] = useState(null); const [contextTarget, setContextTarget] = useState(null); @@ -130,8 +132,14 @@ export function AccountsTable({ data, defaultAccount }: AccountsTableProps) { size: 220, cell: ({ row }) => { const isDefault = row.original.name === defaultAccount; - const isPending = setDefaultMutation.isPending || deleteMutation.isPending; + const isPending = + setDefaultMutation.isPending || + deleteMutation.isPending || + updateContextMutation.isPending; const isCliproxy = row.original.type === 'cliproxy'; + const isShared = row.original.context_mode === 'shared'; + const hasLegacyInference = + row.original.context_inferred || row.original.continuity_inferred; return (
@@ -147,6 +155,63 @@ export function AccountsTable({ data, defaultAccount }: AccountsTableProps) { )} + {!isCliproxy && ( + + )} + {!isCliproxy && hasLegacyInference && ( + + )} +
@@ -176,7 +210,7 @@ export function AccountsPage() {

Migration Follow-up

-
+
@@ -196,6 +230,18 @@ export function AccountsPage() { )}
+ +
)} @@ -222,19 +268,29 @@ export function AccountsPage() {
ccs auth Workspace History Sync Controls +

Auth Accounts

This table is intentionally scoped to ccs auth - accounts. Edit each account for isolated, shared-standard, or shared-deeper - continuity behavior. + accounts. Use + Link/ + Unlink + for quick policy changes and pencil edit for advanced group/deeper settings.

-
+
{cliproxyCount > 0 && ( - + CLIProxy pool accounts are managed in their own page @@ -246,15 +302,15 @@ export function AccountsPage() { )} - + Account Matrix - Shared total: {sharedCount}. Update sync behavior from the pencil action in each - row. + Shared total: {sharedCount}. Actions now include quick link/unlink plus legacy + confirmation. - + {isLoading ? (
Loading accounts...
) : ( @@ -265,64 +321,137 @@ export function AccountsPage() {
- {/* Right guidance column */} -
-
-

Continuity Guide

-

- Choose the lightest mode that solves your workflow. -

-
- - -
- - - Shared Standard - Project workspace sync only. - - - Best default when users need continuity but want minimal coupling. - - - - - - Shared Deeper (Advanced) - - Adds session-env, file-history,{' '} - shell-snapshots, todos. - - - - Use only when cross-account continuity is worth stronger coupling. - - - - - - Quick Commands - Copy and run in terminal. - - -
- - ccs auth create work --context-group sprint-a --deeper-continuity - - -
-
- ccs cliproxy auth claude - -
-
-
+ {/* Right action center */} + {showGuideRail ? ( +
+
+
+

Action Center

+

+ High-value actions for pool auth and legacy cleanup. +

+
+
- -
+ + +
+ + + Immediate Actions + + + + + + + + + + + + + + + + + +
+

Shared Standard

+

+ Project workspace sync only. Best default for most teams. +

+
+
+

Shared Deeper

+

+ Adds session-env, file-history,{' '} + shell-snapshots, todos. +

+
+
+

Isolated

+

No link. Best for strict separation.

+
+
+
+
+
+ + + + Quick Commands + Copy and run in terminal. + + +
+ + ccs auth create work --context-group sprint-a --deeper-continuity + + +
+
+ ccs cliproxy auth claude + +
+
+
+
+
+
+ ) : ( +
+ +
+ )}
@@ -342,10 +471,24 @@ export function AccountsPage() { Create Account - + + diff --git a/ui/src/pages/cliproxy.tsx b/ui/src/pages/cliproxy.tsx index 5f6229b7..345f0fd0 100644 --- a/ui/src/pages/cliproxy.tsx +++ b/ui/src/pages/cliproxy.tsx @@ -4,7 +4,7 @@ * Right panel: Provider Editor with split-view (settings + code editor) */ -import { useState, useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; @@ -32,6 +32,7 @@ import { } from '@/hooks/use-cliproxy'; import type { AuthStatus, Variant } from '@/lib/api-client'; import { MODEL_CATALOGS } from '@/lib/model-catalogs'; +import { getProviderDisplayName, isValidProvider } from '@/lib/provider-config'; import { cn } from '@/lib/utils'; // Sidebar provider item @@ -198,9 +199,14 @@ export function CliproxyPage() { const deleteMutation = useDeleteVariant(); // Selection state: either a provider or a variant - // Initialize from localStorage if available + // Initialize from URL provider deep-link, fallback to localStorage. const [selectedProvider, setSelectedProviderState] = useState(() => { if (typeof window !== 'undefined') { + const query = new URLSearchParams(window.location.search); + const queryProvider = query.get('provider')?.trim().toLowerCase(); + if (queryProvider && isValidProvider(queryProvider)) { + return queryProvider; + } return localStorage.getItem('cliproxy-selected-provider'); } return null; @@ -211,7 +217,25 @@ export function CliproxyPage() { provider: string; displayName: string; isFirstAccount: boolean; - } | null>(null); + } | null>(() => { + if (typeof window === 'undefined') { + return null; + } + + const query = new URLSearchParams(window.location.search); + const queryProvider = query.get('provider')?.trim().toLowerCase(); + const action = query.get('action'); + + if (action !== 'auth' || !queryProvider || !isValidProvider(queryProvider)) { + return null; + } + + return { + provider: queryProvider, + displayName: getProviderDisplayName(queryProvider), + isFirstAccount: false, + }; + }); const providers = useMemo(() => authData?.authStatus || [], [authData?.authStatus]); const isRemoteMode = authData?.source === 'remote';