@burdenoff/microfe-vibecontrols 2026.608.2 → 2026.608.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"NotesTabPanel.js","names":[],"sources":["../../../src/components/notes/NotesTabPanel.tsx"],"sourcesContent":["import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useState, useMemo } from 'react';\nimport { Plus, FileText, Grid, List, Pin, RefreshCw, Lock, Globe } from 'lucide-react';\nimport { AnimatePresence } from 'framer-motion';\nimport { useNavigate } from 'react-router-dom';\nimport { useApolloClient } from '@apollo/client/react';\nimport { MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { NoteCard } from './NoteCard';\nimport { NoteList } from './NoteList';\nimport { NoteEditor } from './NoteEditor';\nimport type { Note, NoteVisibility } from './types';\nimport type { NoteEditorSubmitData } from './NoteEditor';\nimport {\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n QuotaExhaustedDialog,\n} from '@/components/shared';\nimport {\n useListNotesQuery,\n usePinNoteMutation,\n useUnpinNoteMutation,\n useDeleteNoteMutation,\n useCreateNoteMutation,\n useUpdateNoteMutation,\n useReplaceNoteTagsMutation,\n useAvailableNoteTagsQuery,\n useShareNoteMutation,\n ListNotesDocument,\n} from '@/generated/wspace-operations';\nimport { NoteVisibility as GqlNoteVisibility } from '@/generated/wspace-types';\nimport { PermissionButton, AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlag } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { isQuotaExhaustedError, markQuotaHandledLocally } from '@/utils/quotaUtils';\nimport { NoteShareDialog, SHARE_REGISTRY } from '@/components/sharing';\n\ntype ViewMode = 'grid' | 'list';\n\ninterface NotesTabPanelProps {\n /** 'vibe' = single vibe context; 'agent' = multi-vibe agent context */\n context: 'vibe' | 'agent';\n /** For vibe context: the single vibeId to scope notes to */\n vibeId?: string;\n /** For agent context: the vibes belonging to the agent */\n agentVibes?: { id: string; name: string }[];\n}\n\n/**\n * Reusable notes panel for embedding as a tab in detail pages.\n * Supports vibe-scoped and agent-scoped contexts.\n */\nexport function NotesTabPanel({ context, vibeId, agentVibes = [] }: NotesTabPanelProps) {\n const { t } = useI18n();\n const navigate = useNavigate();\n const apolloClient = useApolloClient();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n\n const canListNotes = hasPermission(VIBE_PERMISSIONS.NOTE_LIST);\n const canCreateNote = hasPermission(VIBE_PERMISSIONS.NOTE_CREATE);\n\n // Generic note sharing (full users/link/vibe dialog). Gated by the\n // `vibecontrols.sharing.note` flag (default ON) + note share permission.\n // Distinct from the legacy `onShare` visibility toggle, which stays as-is.\n const noteSharingFlagEnabled = useFeatureFlag(\n SHARE_REGISTRY.note.featureFlag,\n SHARE_REGISTRY.note.defaultEnabled\n );\n const canShareNote = hasPermission(SHARE_REGISTRY.note.permission);\n const noteSharingEnabled = noteSharingFlagEnabled && canShareNote;\n const [genericShareNote, setGenericShareNote] = useState<Note | null>(null);\n\n const [searchQuery, setSearchQuery] = useState('');\n const [vibeFilter, setVibeFilter] = useState('all');\n const [tagFilter, setTagFilter] = useState('all');\n const [visibilityFilter, setVisibilityFilter] = useState<'all' | 'PRIVATE' | 'WORKSPACE'>('all');\n const [showPinnedOnly, setShowPinnedOnly] = useState(false);\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [showEditor, setShowEditor] = useState(false);\n const [selectedNote, setSelectedNote] = useState<Note | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n\n // Evict cached notes list and refetch — bypasses stale cache-and-network data\n const evictAndRefetch = (refetch: () => void) => {\n apolloClient.cache.evict({ id: 'ROOT_QUERY', fieldName: 'notes' });\n apolloClient.cache.gc();\n refetch();\n };\n\n const [pinNote] = usePinNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [unpinNote] = useUnpinNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [deleteNote] = useDeleteNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [createNote] = useCreateNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [updateNote] = useUpdateNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [replaceNoteTags] = useReplaceNoteTagsMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [shareNote] = useShareNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n\n const shouldSkipQueries = permissionsLoading || !canListNotes;\n\n const { data: availableTagsData } = useAvailableNoteTagsQuery({ skip: shouldSkipQueries });\n\n // Build query filter based on context\n const queryFilter = useMemo(() => {\n const visibilityVal =\n visibilityFilter !== 'all' ? (visibilityFilter as GqlNoteVisibility) : undefined;\n\n if (context === 'vibe' && vibeId) {\n return {\n vibeId,\n isPinned: showPinnedOnly ? true : undefined,\n visibility: visibilityVal,\n };\n }\n // Agent context: fetch all notes, filter client-side\n return {\n vibeId: vibeFilter === 'none' ? null : vibeFilter !== 'all' ? vibeFilter : undefined,\n isPinned: showPinnedOnly ? true : undefined,\n visibility: visibilityVal,\n };\n }, [context, vibeId, vibeFilter, showPinnedOnly, visibilityFilter]);\n\n const { data, loading, error, refetch } = useListNotesQuery({\n variables: { filter: queryFilter, pagination: { limit: 50 } },\n fetchPolicy: 'cache-and-network',\n skip: shouldSkipQueries,\n });\n\n // Vibe filter options (agent context only)\n const vibeOptions = useMemo(() => {\n if (context !== 'agent') return [];\n return [\n { value: 'all', label: 'All Vibes' },\n { value: 'none', label: 'No Vibe (General)' },\n ...agentVibes.map((v) => ({ value: v.id, label: v.name })),\n ];\n }, [context, agentVibes]);\n\n // Tag label lookup by ID — used when federation doesn't return nested tag object\n type AvailableTag = { id: string; key: string; label: string; color?: string | null };\n const tagLabelMap = useMemo((): Map<string, AvailableTag> => {\n const tags = (availableTagsData?.availableNoteTags ?? []) as AvailableTag[];\n return new Map(tags.map((tag) => [tag.id, tag]));\n }, [availableTagsData?.availableNoteTags]);\n\n // Map GraphQL response to Note type\n const notes: Note[] = useMemo(() => {\n const rawNotes = data?.notes?.items ?? [];\n const mapped = rawNotes.map((note: NonNullable<typeof rawNotes>[number]) => ({\n id: note.id,\n workspaceId: note.workspaceId,\n vibeId: note.vibeId || null,\n title: note.title,\n content: note.content,\n tags: note.tags || [],\n tagAssignments:\n note.tagAssignments?.map((a: NonNullable<typeof note.tagAssignments>[number]) => {\n // `a.tag` may be null when federation doesn't resolve the tag entity.\n // Fall back to the availableNoteTags lookup so the label always shows.\n const resolvedTag = a.tag\n ? { id: a.tag.id, label: a.tag.label, key: a.tag.key, color: a.tag.color }\n : tagLabelMap.has(a.tagId)\n ? {\n id: a.tagId,\n label: tagLabelMap.get(a.tagId)!.label,\n key: tagLabelMap.get(a.tagId)!.key,\n color: tagLabelMap.get(a.tagId)!.color ?? null,\n }\n : undefined;\n return {\n id: a.id,\n tagId: a.tagId,\n tag: resolvedTag,\n entityType: a.entityType,\n entityId: a.entityId,\n source: a.source,\n assignedBy: a.assignedBy,\n assignedAt: a.assignedAt,\n };\n }) || [],\n isPinned: note.isPinned,\n visibility: (note.visibility ?? 'WORKSPACE') as NoteVisibility,\n createdBy: note.createdBy ?? '',\n metadata: (note.metadata as Record<string, unknown>) || {},\n createdAt: note.createdAt,\n updatedAt: note.updatedAt,\n vibe: note.vibe ? { id: note.vibe.id, name: note.vibe.name } : undefined,\n }));\n\n // Agent context: client-side filter to only agent's vibes\n if (context === 'agent' && agentVibes.length > 0) {\n const vibeIdSet = new Set(agentVibes.map((v) => v.id));\n return mapped.filter((n: Note) => n.vibeId && vibeIdSet.has(n.vibeId));\n }\n\n return mapped;\n }, [data?.notes?.items, context, agentVibes, tagLabelMap]);\n\n // Tag filter options\n const tagOptions = useMemo(() => {\n const tags = availableTagsData?.availableNoteTags ?? [];\n return [\n { value: 'all', label: 'All Tags' },\n ...tags.map((tag: AvailableTag) => ({\n value: tag.id,\n label: tag.label,\n })),\n ];\n }, [availableTagsData?.availableNoteTags]);\n\n // Client-side search and tag filtering\n const filteredNotes = useMemo(() => {\n return notes.filter((note) => {\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n const matchesSearch =\n note.title.toLowerCase().includes(query) ||\n note.content.toLowerCase().includes(query) ||\n note.tagAssignments?.some((a) => a.tag?.label?.toLowerCase().includes(query));\n if (!matchesSearch) return false;\n }\n if (tagFilter !== 'all') {\n const hasTag = note.tagAssignments?.some((a) => a.tagId === tagFilter);\n if (!hasTag) return false;\n }\n return true;\n });\n }, [notes, searchQuery, tagFilter]);\n\n // Sort: pinned first, then by updatedAt\n const sortedNotes = useMemo(() => {\n return [...filteredNotes].sort((a, b) => {\n if (a.isPinned && !b.isPinned) return -1;\n if (!a.isPinned && b.isPinned) return 1;\n return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();\n });\n }, [filteredNotes]);\n\n const handleNoteSelect = (note: Note) => {\n setSelectedNote(note);\n setShowEditor(true);\n };\n\n const handleCreateNote = () => {\n setSelectedNote(null);\n setShowEditor(true);\n };\n\n const handlePinNote = async (note: Note) => {\n try {\n if (note.isPinned) {\n await unpinNote({ variables: { id: note.id } });\n } else {\n await pinNote({ variables: { id: note.id } });\n }\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to toggle pin for note:', err);\n }\n };\n\n const handleDeleteNote = async (note: Note) => {\n try {\n await deleteNote({ variables: { id: note.id } });\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to delete note:', err);\n }\n };\n\n const handleSaveNote = async (noteData: NoteEditorSubmitData) => {\n const closeEditorForQuota = () => {\n setShowEditor(false);\n setSelectedNote(null);\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n };\n\n try {\n let noteId: string | undefined;\n\n // In vibe context, always force the vibeId\n const effectiveVibeId = context === 'vibe' ? vibeId : noteData.vibeId;\n\n if (noteData.id) {\n let result;\n try {\n result = await updateNote({\n variables: {\n id: noteData.id,\n input: {\n title: noteData.title,\n content: noteData.content,\n visibility: noteData.visibility\n ? (noteData.visibility as GqlNoteVisibility)\n : undefined,\n },\n },\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n closeEditorForQuota();\n return;\n }\n\n throw err;\n }\n noteId = result.data?.updateNote?.id;\n } else {\n let result;\n try {\n result = await createNote({\n variables: {\n input: {\n title: noteData.title || '',\n content: noteData.content || '',\n vibeId: effectiveVibeId,\n visibility: noteData.visibility\n ? (noteData.visibility as GqlNoteVisibility)\n : GqlNoteVisibility.Workspace,\n },\n },\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n closeEditorForQuota();\n return;\n }\n\n throw err;\n }\n noteId = result.data?.createNote?.id;\n }\n\n // Assign tags\n if (noteId && noteData.selectedTagIds && noteData.selectedTagIds.length > 0) {\n await replaceNoteTags({\n variables: { input: { noteId, tagIds: noteData.selectedTagIds } },\n });\n } else if (noteId && noteData.selectedTagIds?.length === 0 && noteData.id) {\n await replaceNoteTags({\n variables: { input: { noteId, tagIds: [] } },\n });\n }\n\n setShowEditor(false);\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to save note:', err);\n }\n };\n\n const handleShareNote = async (note: Note) => {\n const newVisibility =\n note.visibility === 'PRIVATE' ? GqlNoteVisibility.Workspace : GqlNoteVisibility.Private;\n try {\n await shareNote({ variables: { id: note.id, visibility: newVisibility } });\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to toggle note visibility:', err);\n }\n };\n\n // Opens the generic Share dialog for a note (users / link / vibe).\n const handleShareNoteAccess = noteSharingEnabled\n ? (note: Note) => setGenericShareNote(note)\n : undefined;\n\n // Stats\n const stats = {\n total: notes.length,\n pinned: notes.filter((n) => n.isPinned).length,\n };\n\n // Vibe options for editor\n const vibeOptionsForEditor = useMemo(() => {\n if (context === 'vibe') return [];\n return agentVibes;\n }, [context, agentVibes]);\n\n // Permission guard\n if (!permissionsLoading && permissions && !canListNotes) {\n return (\n <AccessDenied\n title=\"Access Denied\"\n description=\"You don't have permission to view notes.\"\n details=\"Required permission: note:list\"\n />\n );\n }\n\n if (loading && notes.length === 0) {\n return <LoadingState message=\"Loading notes...\" />;\n }\n\n if (error && notes.length === 0) {\n return (\n <ErrorState title=\"Failed to load notes\" message={error.message} onRetry={() => refetch()} />\n );\n }\n\n const isFiltered =\n searchQuery ||\n (context === 'agent' && vibeFilter !== 'all') ||\n tagFilter !== 'all' ||\n showPinnedOnly;\n\n return (\n <div className=\"space-y-4\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">Notes</h3>\n <p className=\"text-sm text-text-secondary\">\n {stats.total} {stats.total === 1 ? 'note' : 'notes'}, {stats.pinned} pinned\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title=\"Refresh notes\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <PermissionButton permission={VIBE_PERMISSIONS.NOTE_CREATE}>\n <button\n type=\"button\"\n onClick={handleCreateNote}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n New Note\n </button>\n </PermissionButton>\n </div>\n </div>\n\n {/* Filters */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder=\"Search notes...\"\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'grid' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title=\"Grid view\"\n >\n <Grid className=\"size-4 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'list' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title=\"List view\"\n >\n <List className=\"size-4 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n {context === 'agent' && (\n <FilterDropdown\n label=\"Vibe\"\n value={vibeFilter}\n options={vibeOptions}\n onChange={setVibeFilter}\n />\n )}\n <FilterDropdown\n label=\"Tag\"\n value={tagFilter}\n options={tagOptions}\n onChange={setTagFilter}\n />\n {/* Visibility filter */}\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-0.5\">\n <button\n type=\"button\"\n onClick={() => setVisibilityFilter('all')}\n className={`px-2.5 py-1 text-xs rounded transition-colors ${visibilityFilter === 'all' ? 'bg-bg-surface text-text-primary shadow-sm' : 'text-text-muted hover:text-text-secondary'}`}\n >\n All\n </button>\n <button\n type=\"button\"\n onClick={() => setVisibilityFilter('WORKSPACE')}\n className={`flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors ${visibilityFilter === 'WORKSPACE' ? 'bg-bg-surface text-text-primary shadow-sm' : 'text-text-muted hover:text-text-secondary'}`}\n >\n <Globe className=\"size-3\" />\n Shared\n </button>\n <button\n type=\"button\"\n onClick={() => setVisibilityFilter('PRIVATE')}\n className={`flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors ${visibilityFilter === 'PRIVATE' ? 'bg-bg-surface text-text-primary shadow-sm' : 'text-text-muted hover:text-text-secondary'}`}\n >\n <Lock className=\"size-3\" />\n Private\n </button>\n </div>\n </div>\n </div>\n\n {/* Pinned toggle */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setShowPinnedOnly(!showPinnedOnly)}\n className={`flex items-center gap-2 px-3 py-1.5 text-sm rounded-full transition-colors ${\n showPinnedOnly\n ? 'bg-status-warning-bg text-status-warning-text'\n : 'bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80'\n }`}\n >\n <Pin className=\"size-3\" />\n Pinned ({stats.pinned})\n </button>\n </div>\n\n {/* Results count */}\n {isFiltered ? (\n <p className=\"text-sm text-text-secondary\">\n Showing {sortedNotes.length} of {notes.length} notes\n {loading && <span className=\"ml-2 text-text-muted\">(refreshing…)</span>}\n </p>\n ) : null}\n\n {/* Notes display */}\n <AnimatePresence mode=\"wait\">\n {sortedNotes.length > 0 ? (\n viewMode === 'grid' ? (\n <MotionList\n key=\"grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {sortedNotes.map((note) => (\n <MotionItem key={note.id}>\n <NoteCard\n note={note}\n onOpen={handleNoteSelect}\n onEdit={handleNoteSelect}\n onPin={handlePinNote}\n onUnpin={handlePinNote}\n onDelete={handleDeleteNote}\n onShare={handleShareNote}\n onShareAccess={handleShareNoteAccess}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <NoteList\n key=\"list\"\n notes={sortedNotes}\n onOpen={handleNoteSelect}\n onEdit={handleNoteSelect}\n onPin={handlePinNote}\n onUnpin={handlePinNote}\n onDelete={handleDeleteNote}\n onShare={handleShareNote}\n onShareAccess={handleShareNoteAccess}\n />\n )\n ) : (\n <EmptyState\n icon={<FileText className=\"size-8 text-text-secondary\" />}\n title={isFiltered ? 'No notes found' : 'No notes yet'}\n description={\n isFiltered\n ? 'Try adjusting your search or filters'\n : 'Create your first note to start documenting your work'\n }\n action={\n !isFiltered && canCreateNote ? (\n <button\n type=\"button\"\n onClick={handleCreateNote}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n New Note\n </button>\n ) : undefined\n }\n />\n )}\n </AnimatePresence>\n\n {/* Note Editor Modal */}\n {showEditor && (\n <NoteEditor\n note={selectedNote || undefined}\n vibeOptions={vibeOptionsForEditor}\n defaultVibeId={context === 'vibe' ? vibeId : undefined}\n hideVibeSelect={context === 'vibe'}\n onSubmit={handleSaveNote}\n onCancel={() => setShowEditor(false)}\n />\n )}\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title=\"Note Quota Reached\"\n message=\"You've used all available note quota on your current plan. Upgrade your plan to create or update more notes.\"\n />\n\n {/* Generic note sharing dialog (users / link / vibe) */}\n {genericShareNote && (\n <NoteShareDialog\n note={{\n id: genericShareNote.id,\n title: genericShareNote.title,\n vibeId: genericShareNote.vibeId,\n }}\n open={Boolean(genericShareNote)}\n onOpenChange={(next) => {\n if (!next) setGenericShareNote(null);\n }}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"NotesTabPanel.js","names":[],"sources":["../../../src/components/notes/NotesTabPanel.tsx"],"sourcesContent":["import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useState, useMemo } from 'react';\nimport { Plus, FileText, Grid, List, Pin, RefreshCw, Lock, Globe } from 'lucide-react';\nimport { AnimatePresence } from 'framer-motion';\nimport { useNavigate } from 'react-router-dom';\nimport { useApolloClient } from '@apollo/client/react';\nimport { MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { NoteCard } from './NoteCard';\nimport { NoteList } from './NoteList';\nimport { NoteEditor } from './NoteEditor';\nimport type { Note, NoteVisibility } from './types';\nimport type { NoteEditorSubmitData } from './NoteEditor';\nimport {\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n QuotaExhaustedDialog,\n} from '@/components/shared';\nimport {\n useListNotesQuery,\n usePinNoteMutation,\n useUnpinNoteMutation,\n useDeleteNoteMutation,\n useCreateNoteMutation,\n useUpdateNoteMutation,\n useReplaceNoteTagsMutation,\n useAvailableNoteTagsQuery,\n useShareNoteMutation,\n ListNotesDocument,\n} from '@/generated/wspace-operations';\nimport { NoteVisibility as GqlNoteVisibility } from '@/generated/wspace-types';\nimport { PermissionButton, AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useFeatureFlag } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { isQuotaExhaustedError, markQuotaHandledLocally } from '@/utils/quotaUtils';\nimport { NoteShareDialog, SHARE_REGISTRY } from '@/components/sharing';\n\ntype ViewMode = 'grid' | 'list';\n\ninterface NotesTabPanelProps {\n /**\n * 'vibe' = single vibe context; 'agent' = multi-vibe agent context;\n * 'session' = notes authored against a single terminal session (F1).\n */\n context: 'vibe' | 'agent' | 'session';\n /** For vibe context: the single vibeId to scope notes to */\n vibeId?: string;\n /** For session context: the single sessionId to scope notes to */\n sessionId?: string;\n /** For agent context: the vibes belonging to the agent */\n agentVibes?: { id: string; name: string }[];\n}\n\n/**\n * Reusable notes panel for embedding as a tab in detail pages.\n * Supports vibe-scoped, agent-scoped, and session-scoped contexts.\n */\nexport function NotesTabPanel({ context, vibeId, sessionId, agentVibes = [] }: NotesTabPanelProps) {\n const { t } = useI18n();\n const navigate = useNavigate();\n const apolloClient = useApolloClient();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n\n const canListNotes = hasPermission(VIBE_PERMISSIONS.NOTE_LIST);\n const canCreateNote = hasPermission(VIBE_PERMISSIONS.NOTE_CREATE);\n\n // Generic note sharing (full users/link/vibe dialog). Gated by the\n // `vibecontrols.sharing.note` flag (default ON) + note share permission.\n // Distinct from the legacy `onShare` visibility toggle, which stays as-is.\n const noteSharingFlagEnabled = useFeatureFlag(\n SHARE_REGISTRY.note.featureFlag,\n SHARE_REGISTRY.note.defaultEnabled\n );\n const canShareNote = hasPermission(SHARE_REGISTRY.note.permission);\n const noteSharingEnabled = noteSharingFlagEnabled && canShareNote;\n const [genericShareNote, setGenericShareNote] = useState<Note | null>(null);\n\n const [searchQuery, setSearchQuery] = useState('');\n const [vibeFilter, setVibeFilter] = useState('all');\n const [tagFilter, setTagFilter] = useState('all');\n const [visibilityFilter, setVisibilityFilter] = useState<'all' | 'PRIVATE' | 'WORKSPACE'>('all');\n const [showPinnedOnly, setShowPinnedOnly] = useState(false);\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [showEditor, setShowEditor] = useState(false);\n const [selectedNote, setSelectedNote] = useState<Note | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n\n // Evict cached notes list and refetch — bypasses stale cache-and-network data\n const evictAndRefetch = (refetch: () => void) => {\n apolloClient.cache.evict({ id: 'ROOT_QUERY', fieldName: 'notes' });\n apolloClient.cache.gc();\n refetch();\n };\n\n const [pinNote] = usePinNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [unpinNote] = useUnpinNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [deleteNote] = useDeleteNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [createNote] = useCreateNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [updateNote] = useUpdateNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [replaceNoteTags] = useReplaceNoteTagsMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n const [shareNote] = useShareNoteMutation({\n refetchQueries: [ListNotesDocument],\n awaitRefetchQueries: true,\n });\n\n const shouldSkipQueries = permissionsLoading || !canListNotes;\n\n const { data: availableTagsData } = useAvailableNoteTagsQuery({ skip: shouldSkipQueries });\n\n // Build query filter based on context\n const queryFilter = useMemo(() => {\n const visibilityVal =\n visibilityFilter !== 'all' ? (visibilityFilter as GqlNoteVisibility) : undefined;\n\n if (context === 'session' && sessionId) {\n return {\n sessionId,\n isPinned: showPinnedOnly ? true : undefined,\n visibility: visibilityVal,\n };\n }\n if (context === 'vibe' && vibeId) {\n return {\n vibeId,\n isPinned: showPinnedOnly ? true : undefined,\n visibility: visibilityVal,\n };\n }\n // Agent context: fetch all notes, filter client-side\n return {\n vibeId: vibeFilter === 'none' ? null : vibeFilter !== 'all' ? vibeFilter : undefined,\n isPinned: showPinnedOnly ? true : undefined,\n visibility: visibilityVal,\n };\n }, [context, vibeId, sessionId, vibeFilter, showPinnedOnly, visibilityFilter]);\n\n const { data, loading, error, refetch } = useListNotesQuery({\n variables: { filter: queryFilter, pagination: { limit: 50 } },\n fetchPolicy: 'cache-and-network',\n skip: shouldSkipQueries,\n });\n\n // Vibe filter options (agent context only)\n const vibeOptions = useMemo(() => {\n if (context !== 'agent') return [];\n return [\n { value: 'all', label: 'All Vibes' },\n { value: 'none', label: 'No Vibe (General)' },\n ...agentVibes.map((v) => ({ value: v.id, label: v.name })),\n ];\n }, [context, agentVibes]);\n\n // Tag label lookup by ID — used when federation doesn't return nested tag object\n type AvailableTag = { id: string; key: string; label: string; color?: string | null };\n const tagLabelMap = useMemo((): Map<string, AvailableTag> => {\n const tags = (availableTagsData?.availableNoteTags ?? []) as AvailableTag[];\n return new Map(tags.map((tag) => [tag.id, tag]));\n }, [availableTagsData?.availableNoteTags]);\n\n // Map GraphQL response to Note type\n const notes: Note[] = useMemo(() => {\n const rawNotes = data?.notes?.items ?? [];\n const mapped = rawNotes.map((note: NonNullable<typeof rawNotes>[number]) => ({\n id: note.id,\n workspaceId: note.workspaceId,\n vibeId: note.vibeId || null,\n title: note.title,\n content: note.content,\n tags: note.tags || [],\n tagAssignments:\n note.tagAssignments?.map((a: NonNullable<typeof note.tagAssignments>[number]) => {\n // `a.tag` may be null when federation doesn't resolve the tag entity.\n // Fall back to the availableNoteTags lookup so the label always shows.\n const resolvedTag = a.tag\n ? { id: a.tag.id, label: a.tag.label, key: a.tag.key, color: a.tag.color }\n : tagLabelMap.has(a.tagId)\n ? {\n id: a.tagId,\n label: tagLabelMap.get(a.tagId)!.label,\n key: tagLabelMap.get(a.tagId)!.key,\n color: tagLabelMap.get(a.tagId)!.color ?? null,\n }\n : undefined;\n return {\n id: a.id,\n tagId: a.tagId,\n tag: resolvedTag,\n entityType: a.entityType,\n entityId: a.entityId,\n source: a.source,\n assignedBy: a.assignedBy,\n assignedAt: a.assignedAt,\n };\n }) || [],\n isPinned: note.isPinned,\n visibility: (note.visibility ?? 'WORKSPACE') as NoteVisibility,\n createdBy: note.createdBy ?? '',\n metadata: (note.metadata as Record<string, unknown>) || {},\n createdAt: note.createdAt,\n updatedAt: note.updatedAt,\n vibe: note.vibe ? { id: note.vibe.id, name: note.vibe.name } : undefined,\n }));\n\n // Agent context: client-side filter to only agent's vibes\n if (context === 'agent' && agentVibes.length > 0) {\n const vibeIdSet = new Set(agentVibes.map((v) => v.id));\n return mapped.filter((n: Note) => n.vibeId && vibeIdSet.has(n.vibeId));\n }\n\n return mapped;\n }, [data?.notes?.items, context, agentVibes, tagLabelMap]);\n\n // Tag filter options\n const tagOptions = useMemo(() => {\n const tags = availableTagsData?.availableNoteTags ?? [];\n return [\n { value: 'all', label: 'All Tags' },\n ...tags.map((tag: AvailableTag) => ({\n value: tag.id,\n label: tag.label,\n })),\n ];\n }, [availableTagsData?.availableNoteTags]);\n\n // Client-side search and tag filtering\n const filteredNotes = useMemo(() => {\n return notes.filter((note) => {\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n const matchesSearch =\n note.title.toLowerCase().includes(query) ||\n note.content.toLowerCase().includes(query) ||\n note.tagAssignments?.some((a) => a.tag?.label?.toLowerCase().includes(query));\n if (!matchesSearch) return false;\n }\n if (tagFilter !== 'all') {\n const hasTag = note.tagAssignments?.some((a) => a.tagId === tagFilter);\n if (!hasTag) return false;\n }\n return true;\n });\n }, [notes, searchQuery, tagFilter]);\n\n // Sort: pinned first, then by updatedAt\n const sortedNotes = useMemo(() => {\n return [...filteredNotes].sort((a, b) => {\n if (a.isPinned && !b.isPinned) return -1;\n if (!a.isPinned && b.isPinned) return 1;\n return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();\n });\n }, [filteredNotes]);\n\n const handleNoteSelect = (note: Note) => {\n setSelectedNote(note);\n setShowEditor(true);\n };\n\n const handleCreateNote = () => {\n setSelectedNote(null);\n setShowEditor(true);\n };\n\n const handlePinNote = async (note: Note) => {\n try {\n if (note.isPinned) {\n await unpinNote({ variables: { id: note.id } });\n } else {\n await pinNote({ variables: { id: note.id } });\n }\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to toggle pin for note:', err);\n }\n };\n\n const handleDeleteNote = async (note: Note) => {\n try {\n await deleteNote({ variables: { id: note.id } });\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to delete note:', err);\n }\n };\n\n const handleSaveNote = async (noteData: NoteEditorSubmitData) => {\n const closeEditorForQuota = () => {\n setShowEditor(false);\n setSelectedNote(null);\n markQuotaHandledLocally();\n setShowQuotaExhausted(true);\n };\n\n try {\n let noteId: string | undefined;\n\n // In vibe context, always force the vibeId\n const effectiveVibeId = context === 'vibe' ? vibeId : noteData.vibeId;\n\n if (noteData.id) {\n let result;\n try {\n result = await updateNote({\n variables: {\n id: noteData.id,\n input: {\n title: noteData.title,\n content: noteData.content,\n visibility: noteData.visibility\n ? (noteData.visibility as GqlNoteVisibility)\n : undefined,\n },\n },\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n closeEditorForQuota();\n return;\n }\n\n throw err;\n }\n noteId = result.data?.updateNote?.id;\n } else {\n let result;\n try {\n result = await createNote({\n variables: {\n input: {\n title: noteData.title || '',\n content: noteData.content || '',\n vibeId: effectiveVibeId,\n // F1 — stamp the session so the note shows in this session's\n // drawer; it stays workspace-reusable everywhere else.\n sessionId: context === 'session' ? sessionId : undefined,\n visibility: noteData.visibility\n ? (noteData.visibility as GqlNoteVisibility)\n : GqlNoteVisibility.Workspace,\n },\n },\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n closeEditorForQuota();\n return;\n }\n\n throw err;\n }\n noteId = result.data?.createNote?.id;\n }\n\n // Assign tags\n if (noteId && noteData.selectedTagIds && noteData.selectedTagIds.length > 0) {\n await replaceNoteTags({\n variables: { input: { noteId, tagIds: noteData.selectedTagIds } },\n });\n } else if (noteId && noteData.selectedTagIds?.length === 0 && noteData.id) {\n await replaceNoteTags({\n variables: { input: { noteId, tagIds: [] } },\n });\n }\n\n setShowEditor(false);\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to save note:', err);\n }\n };\n\n const handleShareNote = async (note: Note) => {\n const newVisibility =\n note.visibility === 'PRIVATE' ? GqlNoteVisibility.Workspace : GqlNoteVisibility.Private;\n try {\n await shareNote({ variables: { id: note.id, visibility: newVisibility } });\n evictAndRefetch(refetch);\n } catch (err) {\n console.error('Failed to toggle note visibility:', err);\n }\n };\n\n // Opens the generic Share dialog for a note (users / link / vibe).\n const handleShareNoteAccess = noteSharingEnabled\n ? (note: Note) => setGenericShareNote(note)\n : undefined;\n\n // Stats\n const stats = {\n total: notes.length,\n pinned: notes.filter((n) => n.isPinned).length,\n };\n\n // Vibe options for editor\n const vibeOptionsForEditor = useMemo(() => {\n if (context === 'vibe') return [];\n return agentVibes;\n }, [context, agentVibes]);\n\n // Permission guard\n if (!permissionsLoading && permissions && !canListNotes) {\n return (\n <AccessDenied\n title=\"Access Denied\"\n description=\"You don't have permission to view notes.\"\n details=\"Required permission: note:list\"\n />\n );\n }\n\n if (loading && notes.length === 0) {\n return <LoadingState message=\"Loading notes...\" />;\n }\n\n if (error && notes.length === 0) {\n return (\n <ErrorState title=\"Failed to load notes\" message={error.message} onRetry={() => refetch()} />\n );\n }\n\n const isFiltered =\n searchQuery ||\n (context === 'agent' && vibeFilter !== 'all') ||\n tagFilter !== 'all' ||\n showPinnedOnly;\n\n return (\n <div className=\"space-y-4\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">Notes</h3>\n <p className=\"text-sm text-text-secondary\">\n {stats.total} {stats.total === 1 ? 'note' : 'notes'}, {stats.pinned} pinned\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title=\"Refresh notes\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <PermissionButton permission={VIBE_PERMISSIONS.NOTE_CREATE}>\n <button\n type=\"button\"\n onClick={handleCreateNote}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n New Note\n </button>\n </PermissionButton>\n </div>\n </div>\n\n {/* Filters */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder=\"Search notes...\"\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'grid' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title=\"Grid view\"\n >\n <Grid className=\"size-4 text-text-secondary\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'list' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title=\"List view\"\n >\n <List className=\"size-4 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n {context === 'agent' && (\n <FilterDropdown\n label=\"Vibe\"\n value={vibeFilter}\n options={vibeOptions}\n onChange={setVibeFilter}\n />\n )}\n <FilterDropdown\n label=\"Tag\"\n value={tagFilter}\n options={tagOptions}\n onChange={setTagFilter}\n />\n {/* Visibility filter */}\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-0.5\">\n <button\n type=\"button\"\n onClick={() => setVisibilityFilter('all')}\n className={`px-2.5 py-1 text-xs rounded transition-colors ${visibilityFilter === 'all' ? 'bg-bg-surface text-text-primary shadow-sm' : 'text-text-muted hover:text-text-secondary'}`}\n >\n All\n </button>\n <button\n type=\"button\"\n onClick={() => setVisibilityFilter('WORKSPACE')}\n className={`flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors ${visibilityFilter === 'WORKSPACE' ? 'bg-bg-surface text-text-primary shadow-sm' : 'text-text-muted hover:text-text-secondary'}`}\n >\n <Globe className=\"size-3\" />\n Shared\n </button>\n <button\n type=\"button\"\n onClick={() => setVisibilityFilter('PRIVATE')}\n className={`flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors ${visibilityFilter === 'PRIVATE' ? 'bg-bg-surface text-text-primary shadow-sm' : 'text-text-muted hover:text-text-secondary'}`}\n >\n <Lock className=\"size-3\" />\n Private\n </button>\n </div>\n </div>\n </div>\n\n {/* Pinned toggle */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setShowPinnedOnly(!showPinnedOnly)}\n className={`flex items-center gap-2 px-3 py-1.5 text-sm rounded-full transition-colors ${\n showPinnedOnly\n ? 'bg-status-warning-bg text-status-warning-text'\n : 'bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80'\n }`}\n >\n <Pin className=\"size-3\" />\n Pinned ({stats.pinned})\n </button>\n </div>\n\n {/* Results count */}\n {isFiltered ? (\n <p className=\"text-sm text-text-secondary\">\n Showing {sortedNotes.length} of {notes.length} notes\n {loading && <span className=\"ml-2 text-text-muted\">(refreshing…)</span>}\n </p>\n ) : null}\n\n {/* Notes display */}\n <AnimatePresence mode=\"wait\">\n {sortedNotes.length > 0 ? (\n viewMode === 'grid' ? (\n <MotionList\n key=\"grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {sortedNotes.map((note) => (\n <MotionItem key={note.id}>\n <NoteCard\n note={note}\n onOpen={handleNoteSelect}\n onEdit={handleNoteSelect}\n onPin={handlePinNote}\n onUnpin={handlePinNote}\n onDelete={handleDeleteNote}\n onShare={handleShareNote}\n onShareAccess={handleShareNoteAccess}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <NoteList\n key=\"list\"\n notes={sortedNotes}\n onOpen={handleNoteSelect}\n onEdit={handleNoteSelect}\n onPin={handlePinNote}\n onUnpin={handlePinNote}\n onDelete={handleDeleteNote}\n onShare={handleShareNote}\n onShareAccess={handleShareNoteAccess}\n />\n )\n ) : (\n <EmptyState\n icon={<FileText className=\"size-8 text-text-secondary\" />}\n title={isFiltered ? 'No notes found' : 'No notes yet'}\n description={\n isFiltered\n ? 'Try adjusting your search or filters'\n : 'Create your first note to start documenting your work'\n }\n action={\n !isFiltered && canCreateNote ? (\n <button\n type=\"button\"\n onClick={handleCreateNote}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n New Note\n </button>\n ) : undefined\n }\n />\n )}\n </AnimatePresence>\n\n {/* Note Editor Modal */}\n {showEditor && (\n <NoteEditor\n note={selectedNote || undefined}\n vibeOptions={vibeOptionsForEditor}\n defaultVibeId={context === 'vibe' ? vibeId : undefined}\n hideVibeSelect={context === 'vibe'}\n onSubmit={handleSaveNote}\n onCancel={() => setShowEditor(false)}\n />\n )}\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing/plans');\n }}\n title=\"Note Quota Reached\"\n message=\"You've used all available note quota on your current plan. Upgrade your plan to create or update more notes.\"\n />\n\n {/* Generic note sharing dialog (users / link / vibe) */}\n {genericShareNote && (\n <NoteShareDialog\n note={{\n id: genericShareNote.id,\n title: genericShareNote.title,\n vibeId: genericShareNote.vibeId,\n }}\n open={Boolean(genericShareNote)}\n onOpenChange={(next) => {\n if (!next) setGenericShareNote(null);\n }}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,231 @@
1
+ import { useListAiPromptsQuery as e, useListVibeAuditLogsQuery as t, useSessionHistoryRecordingEnabledQuery as n, useUpdatePreferencesMutation as r } from "../../generated/wspace-operations.js";
2
+ import { useVibeNavigate as i } from "../../providers/VibeControlsProvider.js";
3
+ import { NotesTabPanel as a } from "../notes/NotesTabPanel.js";
4
+ import { useCallback as o, useMemo as s, useState as c } from "react";
5
+ import { Check as l, Copy as u, History as d, Loader2 as f, Search as p, ShieldOff as m, Sparkles as h, StickyNote as g, Terminal as _, X as v } from "lucide-react";
6
+ import { Fragment as y, jsx as b, jsxs as x } from "react/jsx-runtime";
7
+ //#region src/components/sessions/SessionWorkbenchDrawer.tsx
8
+ function S({ text: e, label: t }) {
9
+ let [n, r] = c(!1);
10
+ return /* @__PURE__ */ x("button", {
11
+ type: "button",
12
+ onClick: o(() => {
13
+ navigator.clipboard.writeText(e).then(() => {
14
+ r(!0), window.setTimeout(() => r(!1), 1500);
15
+ });
16
+ }, [e]),
17
+ className: "flex items-center gap-1.5 px-2 py-1 text-xs rounded-md bg-bg-sunken text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors",
18
+ title: "Copy to clipboard — paste into the terminal with Ctrl/Cmd+V",
19
+ children: [n ? /* @__PURE__ */ b(l, { className: "size-3.5 text-status-success-text" }) : /* @__PURE__ */ b(u, { className: "size-3.5" }), n ? "Copied" : t ?? "Copy"]
20
+ });
21
+ }
22
+ function C({ sessionId: l, open: u, onClose: C }) {
23
+ let [w, T] = c("notes"), [E, D] = c(""), O = i(), { data: k, loading: A } = e({
24
+ variables: {
25
+ filter: {},
26
+ pagination: { limit: 100 }
27
+ },
28
+ fetchPolicy: "cache-and-network",
29
+ skip: !u || w !== "prompts"
30
+ }), j = s(() => {
31
+ let e = k?.aiPrompts?.items ?? [], t = E.trim().toLowerCase();
32
+ return t ? e.filter((e) => e.name.toLowerCase().includes(t) || (e.content ?? "").toLowerCase().includes(t) || (e.description ?? "").toLowerCase().includes(t)) : e;
33
+ }, [k, E]), { data: M, loading: N, refetch: P } = n({
34
+ skip: !u || w !== "history",
35
+ fetchPolicy: "cache-and-network"
36
+ }), F = M?.sessionHistoryRecordingEnabled ?? !1, [I, { loading: L }] = r(), R = o(async () => {
37
+ await I({ variables: { input: { sessionHistoryRecording: !0 } } }), await P();
38
+ }, [I, P]), { data: z, loading: B } = t({
39
+ variables: {
40
+ filter: { sessionId: l },
41
+ pagination: { limit: 100 }
42
+ },
43
+ fetchPolicy: "cache-and-network",
44
+ skip: !u || w !== "history" || !F
45
+ }), V = z?.vibeAuditLogs?.items ?? [];
46
+ return u ? /* @__PURE__ */ x("div", {
47
+ className: "fixed inset-y-0 right-0 z-40 flex w-full max-w-md flex-col border-l border-border-default bg-bg-surface shadow-xl",
48
+ role: "complementary",
49
+ "aria-label": "Session workbench",
50
+ children: [
51
+ /* @__PURE__ */ x("div", {
52
+ className: "flex items-center justify-between border-b border-border-default px-4 py-3",
53
+ children: [/* @__PURE__ */ x("div", {
54
+ className: "flex items-center gap-2",
55
+ children: [/* @__PURE__ */ b(_, { className: "size-4 text-text-secondary" }), /* @__PURE__ */ b("h2", {
56
+ className: "text-sm font-semibold text-text-primary",
57
+ children: "Session workbench"
58
+ })]
59
+ }), /* @__PURE__ */ b("button", {
60
+ type: "button",
61
+ onClick: C,
62
+ className: "rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary transition-colors",
63
+ title: "Close (Esc)",
64
+ children: /* @__PURE__ */ b(v, { className: "size-4" })
65
+ })]
66
+ }),
67
+ /* @__PURE__ */ b("div", {
68
+ className: "flex border-b border-border-default",
69
+ children: [
70
+ {
71
+ id: "notes",
72
+ label: "Notes",
73
+ icon: /* @__PURE__ */ b(g, { className: "size-4" })
74
+ },
75
+ {
76
+ id: "prompts",
77
+ label: "Prompts",
78
+ icon: /* @__PURE__ */ b(h, { className: "size-4" })
79
+ },
80
+ {
81
+ id: "history",
82
+ label: "History",
83
+ icon: /* @__PURE__ */ b(d, { className: "size-4" })
84
+ }
85
+ ].map((e) => /* @__PURE__ */ x("button", {
86
+ type: "button",
87
+ onClick: () => T(e.id),
88
+ className: `flex flex-1 items-center justify-center gap-1.5 px-3 py-2 text-xs font-medium transition-colors ${w === e.id ? "border-b-2 border-action-primary-bg text-text-primary" : "text-text-secondary hover:text-text-primary"}`,
89
+ children: [e.icon, e.label]
90
+ }, e.id))
91
+ }),
92
+ /* @__PURE__ */ x("div", {
93
+ className: "flex-1 overflow-y-auto",
94
+ children: [
95
+ w === "notes" && /* @__PURE__ */ b("div", {
96
+ className: "p-3",
97
+ children: /* @__PURE__ */ b(a, {
98
+ context: "session",
99
+ sessionId: l
100
+ })
101
+ }),
102
+ w === "prompts" && /* @__PURE__ */ x("div", {
103
+ className: "flex h-full flex-col",
104
+ children: [/* @__PURE__ */ b("div", {
105
+ className: "border-b border-border-default p-3",
106
+ children: /* @__PURE__ */ x("div", {
107
+ className: "flex items-center gap-2 rounded-md bg-bg-sunken px-2.5 py-1.5",
108
+ children: [/* @__PURE__ */ b(p, { className: "size-3.5 text-text-tertiary" }), /* @__PURE__ */ b("input", {
109
+ "aria-label": "Search prompts",
110
+ value: E,
111
+ onChange: (e) => D(e.target.value),
112
+ placeholder: "Search prompts…",
113
+ className: "w-full bg-transparent text-sm text-text-primary placeholder:text-text-placeholder focus:outline-none"
114
+ })]
115
+ })
116
+ }), /* @__PURE__ */ b("div", {
117
+ className: "flex-1 space-y-2 p-3",
118
+ children: A && j.length === 0 ? /* @__PURE__ */ b("div", {
119
+ className: "flex justify-center py-8",
120
+ children: /* @__PURE__ */ b(f, { className: "size-5 animate-spin text-text-tertiary" })
121
+ }) : j.length === 0 ? /* @__PURE__ */ b("p", {
122
+ className: "py-8 text-center text-sm text-text-secondary",
123
+ children: "No prompts yet. Create reusable prompts in the AI section, then copy them here."
124
+ }) : j.map((e) => /* @__PURE__ */ x("div", {
125
+ className: "rounded-md border border-border-default bg-bg-surface p-3",
126
+ children: [
127
+ /* @__PURE__ */ x("div", {
128
+ className: "mb-1 flex items-start justify-between gap-2",
129
+ children: [/* @__PURE__ */ b("span", {
130
+ className: "text-sm font-medium text-text-primary",
131
+ children: e.name
132
+ }), /* @__PURE__ */ b(S, { text: e.content ?? "" })]
133
+ }),
134
+ e.description && /* @__PURE__ */ b("p", {
135
+ className: "mb-1 text-xs text-text-secondary",
136
+ children: e.description
137
+ }),
138
+ /* @__PURE__ */ b("pre", {
139
+ className: "max-h-24 overflow-y-auto whitespace-pre-wrap break-words rounded bg-bg-sunken p-2 font-mono text-xs text-text-primary",
140
+ children: e.content
141
+ })
142
+ ]
143
+ }, e.id))
144
+ })]
145
+ }),
146
+ w === "history" && /* @__PURE__ */ b("div", {
147
+ className: "p-3",
148
+ children: N ? /* @__PURE__ */ b("div", {
149
+ className: "flex justify-center py-8",
150
+ children: /* @__PURE__ */ b(f, { className: "size-5 animate-spin text-text-tertiary" })
151
+ }) : F ? B && V.length === 0 ? /* @__PURE__ */ b("div", {
152
+ className: "flex justify-center py-8",
153
+ children: /* @__PURE__ */ b(f, { className: "size-5 animate-spin text-text-tertiary" })
154
+ }) : /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ x("div", {
155
+ className: "mb-2 flex items-center justify-between",
156
+ children: [/* @__PURE__ */ x("span", {
157
+ className: "text-xs text-text-secondary",
158
+ children: [
159
+ V.length,
160
+ " event",
161
+ V.length === 1 ? "" : "s"
162
+ ]
163
+ }), /* @__PURE__ */ b("button", {
164
+ type: "button",
165
+ onClick: () => O(`/audit?sessionId=${encodeURIComponent(l)}`),
166
+ className: "text-xs font-medium text-action-primary-text hover:underline",
167
+ children: "Open in Audit Log →"
168
+ })]
169
+ }), V.length === 0 ? /* @__PURE__ */ b("p", {
170
+ className: "py-8 text-center text-sm text-text-secondary",
171
+ children: "No recorded activity for this session yet."
172
+ }) : /* @__PURE__ */ b("ol", {
173
+ className: "space-y-2",
174
+ children: V.map((e) => /* @__PURE__ */ x("li", {
175
+ className: "rounded-md border border-border-default bg-bg-surface p-2.5",
176
+ children: [
177
+ /* @__PURE__ */ x("div", {
178
+ className: "flex items-center justify-between gap-2",
179
+ children: [/* @__PURE__ */ x("span", {
180
+ className: "text-xs font-semibold text-text-primary",
181
+ children: [
182
+ e.action,
183
+ " · ",
184
+ e.resourceType
185
+ ]
186
+ }), /* @__PURE__ */ b("span", {
187
+ className: "text-[11px] text-text-tertiary",
188
+ children: new Date(e.createdAt).toLocaleString()
189
+ })]
190
+ }),
191
+ e.description && /* @__PURE__ */ b("p", {
192
+ className: "mt-0.5 text-xs text-text-secondary",
193
+ children: e.description
194
+ }),
195
+ e.command && /* @__PURE__ */ b("pre", {
196
+ className: "mt-1 overflow-x-auto rounded bg-bg-sunken p-1.5 font-mono text-[11px] text-text-primary",
197
+ children: e.command
198
+ })
199
+ ]
200
+ }, e.id))
201
+ })] }) : /* @__PURE__ */ x("div", {
202
+ className: "flex flex-col items-center gap-2 py-10 text-center",
203
+ children: [
204
+ /* @__PURE__ */ b(m, { className: "size-7 text-text-tertiary" }),
205
+ /* @__PURE__ */ b("p", {
206
+ className: "text-sm font-medium text-text-primary",
207
+ children: "Session history is off"
208
+ }),
209
+ /* @__PURE__ */ b("p", {
210
+ className: "max-w-xs text-xs text-text-secondary",
211
+ children: "Session-history recording is off by default. Enable it for yourself below, or ask a workspace admin to turn it on for everyone. Once on, this session's activity timeline appears here and in the Audit Log — and stays available after the session is killed."
212
+ }),
213
+ /* @__PURE__ */ x("button", {
214
+ type: "button",
215
+ onClick: () => void R(),
216
+ disabled: L,
217
+ className: "mt-1 flex items-center gap-2 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-50",
218
+ children: [L && /* @__PURE__ */ b(f, { className: "size-3.5 animate-spin" }), "Enable session history for me"]
219
+ })
220
+ ]
221
+ })
222
+ })
223
+ ]
224
+ })
225
+ ]
226
+ }) : null;
227
+ }
228
+ //#endregion
229
+ export { C as SessionWorkbenchDrawer };
230
+
231
+ //# sourceMappingURL=SessionWorkbenchDrawer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionWorkbenchDrawer.js","names":[],"sources":["../../../src/components/sessions/SessionWorkbenchDrawer.tsx"],"sourcesContent":["import { useCallback, useMemo, useState, type ReactNode } from 'react';\nimport {\n X,\n StickyNote,\n Sparkles,\n History as HistoryIcon,\n Search,\n Copy,\n Check,\n Loader2,\n ShieldOff,\n Terminal as TerminalIcon,\n} from 'lucide-react';\n\nimport {\n useListAiPromptsQuery,\n useListVibeAuditLogsQuery,\n useSessionHistoryRecordingEnabledQuery,\n useUpdatePreferencesMutation,\n type AiPromptPickerFieldsFragment,\n type VibeAuditLogFieldsFragment,\n} from '@/generated/wspace-operations';\nimport { NotesTabPanel } from '@/components/notes/NotesTabPanel';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\n\ntype WorkbenchTab = 'notes' | 'prompts' | 'history';\n\ninterface SessionWorkbenchDrawerProps {\n sessionId: string;\n /** Whether the drawer is open. */\n open: boolean;\n /** Close handler. */\n onClose: () => void;\n}\n\n/** Small clipboard button — copies `text` and flashes a check for 1.5s. */\nfunction CopyButton({ text, label }: { text: string; label?: string }) {\n const [copied, setCopied] = useState(false);\n const onCopy = useCallback(() => {\n void navigator.clipboard.writeText(text).then(() => {\n setCopied(true);\n window.setTimeout(() => setCopied(false), 1500);\n });\n }, [text]);\n return (\n <button\n type=\"button\"\n onClick={onCopy}\n className=\"flex items-center gap-1.5 px-2 py-1 text-xs rounded-md bg-bg-sunken text-text-secondary hover:text-text-primary hover:bg-bg-elevated transition-colors\"\n title=\"Copy to clipboard — paste into the terminal with Ctrl/Cmd+V\"\n >\n {copied ? (\n <Check className=\"size-3.5 text-status-success-text\" />\n ) : (\n <Copy className=\"size-3.5\" />\n )}\n {copied ? 'Copied' : (label ?? 'Copy')}\n </button>\n );\n}\n\n/**\n * Right-side slide-over that lets a user work with session-scoped notes,\n * reusable AI prompts, and the session's activity history without leaving the\n * terminal. \"Copy\" puts content on the clipboard so it can be pasted into the\n * ttyd terminal (which is a cross-origin iframe — direct injection isn't\n * possible). (F1 + F2)\n */\nexport function SessionWorkbenchDrawer({ sessionId, open, onClose }: SessionWorkbenchDrawerProps) {\n const [tab, setTab] = useState<WorkbenchTab>('notes');\n const [promptSearch, setPromptSearch] = useState('');\n const navigate = useVibeNavigate();\n\n // Prompts — workspace-wide and reusable across every session.\n const { data: promptData, loading: promptsLoading } = useListAiPromptsQuery({\n variables: { filter: {}, pagination: { limit: 100 } },\n fetchPolicy: 'cache-and-network',\n skip: !open || tab !== 'prompts',\n });\n const prompts = useMemo(() => {\n const items: AiPromptPickerFieldsFragment[] = promptData?.aiPrompts?.items ?? [];\n const q = promptSearch.trim().toLowerCase();\n if (!q) return items;\n return items.filter(\n (p: AiPromptPickerFieldsFragment) =>\n p.name.toLowerCase().includes(q) ||\n (p.content ?? '').toLowerCase().includes(q) ||\n (p.description ?? '').toLowerCase().includes(q)\n );\n }, [promptData, promptSearch]);\n\n // History — off by default; gated by the effective recording flag.\n const {\n data: recData,\n loading: recLoading,\n refetch: refetchRecording,\n } = useSessionHistoryRecordingEnabledQuery({\n skip: !open || tab !== 'history',\n fetchPolicy: 'cache-and-network',\n });\n const recordingEnabled = recData?.sessionHistoryRecordingEnabled ?? false;\n\n // Per-user opt-in (the workspace default is admin-controlled in Settings).\n const [updatePreferences, { loading: enabling }] = useUpdatePreferencesMutation();\n const enableForMe = useCallback(async () => {\n await updatePreferences({ variables: { input: { sessionHistoryRecording: true } } });\n await refetchRecording();\n }, [updatePreferences, refetchRecording]);\n const { data: auditData, loading: auditLoading } = useListVibeAuditLogsQuery({\n variables: { filter: { sessionId }, pagination: { limit: 100 } },\n fetchPolicy: 'cache-and-network',\n skip: !open || tab !== 'history' || !recordingEnabled,\n });\n const events: VibeAuditLogFieldsFragment[] = auditData?.vibeAuditLogs?.items ?? [];\n\n if (!open) return null;\n\n const tabs: { id: WorkbenchTab; label: string; icon: ReactNode }[] = [\n { id: 'notes', label: 'Notes', icon: <StickyNote className=\"size-4\" /> },\n { id: 'prompts', label: 'Prompts', icon: <Sparkles className=\"size-4\" /> },\n { id: 'history', label: 'History', icon: <HistoryIcon className=\"size-4\" /> },\n ];\n\n return (\n <div\n className=\"fixed inset-y-0 right-0 z-40 flex w-full max-w-md flex-col border-l border-border-default bg-bg-surface shadow-xl\"\n role=\"complementary\"\n aria-label=\"Session workbench\"\n >\n {/* Header */}\n <div className=\"flex items-center justify-between border-b border-border-default px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n <TerminalIcon className=\"size-4 text-text-secondary\" />\n <h2 className=\"text-sm font-semibold text-text-primary\">Session workbench</h2>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary transition-colors\"\n title=\"Close (Esc)\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n {/* Tabs */}\n <div className=\"flex border-b border-border-default\">\n {tabs.map((t) => (\n <button\n key={t.id}\n type=\"button\"\n onClick={() => setTab(t.id)}\n className={`flex flex-1 items-center justify-center gap-1.5 px-3 py-2 text-xs font-medium transition-colors ${\n tab === t.id\n ? 'border-b-2 border-action-primary-bg text-text-primary'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n {t.icon}\n {t.label}\n </button>\n ))}\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto\">\n {tab === 'notes' && (\n <div className=\"p-3\">\n <NotesTabPanel context=\"session\" sessionId={sessionId} />\n </div>\n )}\n\n {tab === 'prompts' && (\n <div className=\"flex h-full flex-col\">\n <div className=\"border-b border-border-default p-3\">\n <div className=\"flex items-center gap-2 rounded-md bg-bg-sunken px-2.5 py-1.5\">\n <Search className=\"size-3.5 text-text-tertiary\" />\n <input\n aria-label=\"Search prompts\"\n value={promptSearch}\n onChange={(e) => setPromptSearch(e.target.value)}\n placeholder=\"Search prompts…\"\n className=\"w-full bg-transparent text-sm text-text-primary placeholder:text-text-placeholder focus:outline-none\"\n />\n </div>\n </div>\n <div className=\"flex-1 space-y-2 p-3\">\n {promptsLoading && prompts.length === 0 ? (\n <div className=\"flex justify-center py-8\">\n <Loader2 className=\"size-5 animate-spin text-text-tertiary\" />\n </div>\n ) : prompts.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n No prompts yet. Create reusable prompts in the AI section, then copy them here.\n </p>\n ) : (\n prompts.map((p) => (\n <div\n key={p.id}\n className=\"rounded-md border border-border-default bg-bg-surface p-3\"\n >\n <div className=\"mb-1 flex items-start justify-between gap-2\">\n <span className=\"text-sm font-medium text-text-primary\">{p.name}</span>\n <CopyButton text={p.content ?? ''} />\n </div>\n {p.description && (\n <p className=\"mb-1 text-xs text-text-secondary\">{p.description}</p>\n )}\n <pre className=\"max-h-24 overflow-y-auto whitespace-pre-wrap break-words rounded bg-bg-sunken p-2 font-mono text-xs text-text-primary\">\n {p.content}\n </pre>\n </div>\n ))\n )}\n </div>\n </div>\n )}\n\n {tab === 'history' && (\n <div className=\"p-3\">\n {recLoading ? (\n <div className=\"flex justify-center py-8\">\n <Loader2 className=\"size-5 animate-spin text-text-tertiary\" />\n </div>\n ) : !recordingEnabled ? (\n <div className=\"flex flex-col items-center gap-2 py-10 text-center\">\n <ShieldOff className=\"size-7 text-text-tertiary\" />\n <p className=\"text-sm font-medium text-text-primary\">Session history is off</p>\n <p className=\"max-w-xs text-xs text-text-secondary\">\n Session-history recording is off by default. Enable it for yourself below, or ask\n a workspace admin to turn it on for everyone. Once on, this session&apos;s\n activity timeline appears here and in the Audit Log — and stays available after\n the session is killed.\n </p>\n <button\n type=\"button\"\n onClick={() => void enableForMe()}\n disabled={enabling}\n className=\"mt-1 flex items-center gap-2 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-50\"\n >\n {enabling && <Loader2 className=\"size-3.5 animate-spin\" />}\n Enable session history for me\n </button>\n </div>\n ) : auditLoading && events.length === 0 ? (\n <div className=\"flex justify-center py-8\">\n <Loader2 className=\"size-5 animate-spin text-text-tertiary\" />\n </div>\n ) : (\n <>\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary\">\n {events.length} event{events.length === 1 ? '' : 's'}\n </span>\n <button\n type=\"button\"\n onClick={() => navigate(`/audit?sessionId=${encodeURIComponent(sessionId)}`)}\n className=\"text-xs font-medium text-action-primary-text hover:underline\"\n >\n Open in Audit Log →\n </button>\n </div>\n {events.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n No recorded activity for this session yet.\n </p>\n ) : (\n <ol className=\"space-y-2\">\n {events.map((e) => (\n <li\n key={e.id}\n className=\"rounded-md border border-border-default bg-bg-surface p-2.5\"\n >\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-xs font-semibold text-text-primary\">\n {e.action} · {e.resourceType}\n </span>\n <span className=\"text-[11px] text-text-tertiary\">\n {new Date(e.createdAt).toLocaleString()}\n </span>\n </div>\n {e.description && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">{e.description}</p>\n )}\n {e.command && (\n <pre className=\"mt-1 overflow-x-auto rounded bg-bg-sunken p-1.5 font-mono text-[11px] text-text-primary\">\n {e.command}\n </pre>\n )}\n </li>\n ))}\n </ol>\n )}\n </>\n )}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -877,7 +877,7 @@ function _({ tabs: _, activeTabId: Se, onTabSelect: Ce, onTabClose: v, onOpenExt
877
877
  ]
878
878
  }), document.body) : null : /* @__PURE__ */ g("div", {
879
879
  ref: Qe,
880
- className: `${D ? "flex flex-col h-full" : ""} border-t border-border-default bg-bg-surface ${y && _.length === 0 && !D ? "hidden" : ""}`,
880
+ className: `${D ? "flex flex-col flex-1 min-h-0" : ""} border-t border-border-default bg-bg-surface ${y && _.length === 0 && !D ? "hidden" : ""}`,
881
881
  children: [
882
882
  !D && !y && _.length > 0 && /* @__PURE__ */ h("div", {
883
883
  onMouseDown: Ct,