- Change Reset All button visibility from Variables-tab-only to both tabs,
displaying when either variables or artifacts have data
- Invalidate sandbox files cache in deleteAllInspectorVars alongside
existing conversation/system var invalidations
The Skill view is locked (ViewPicker disabled) while a workflow
is running or chatflow is responding, so ArtifactsSection is never
mounted during runs. Polling there is dead code.
Add conditional refetchInterval to Artifacts components so the file
list refreshes automatically while a workflow debug run or chatflow
preview is in progress, stopping once the run completes.
Add a unified isPreviewable flag to useFileTypeInfo that guards against
rendering binary files as text in both skill artifacts and variable
inspect artifacts preview. Upgrade extension arrays to Sets for O(1)
lookups.
Split SkillSaveContext and useSkillSaveManager into a separate file to
fix react-refresh/only-export-components lint error. Destructure
mutateAsync from useUpdateAppAssetFileContent for a stable callback
reference, preventing unnecessary useCallback cascade rebuilds. Extract
shared patchFileContentCache helper to unify setQueryData logic between
updateCachedContent and the collaboration event handler.
The backend route /apps/{app_id}/sandbox/files expects the actual app ID
as the URL parameter and derives sandbox_id from the logged-in user
internally. The frontend was incorrectly passing userProfile.id (user ID)
as the appId, resulting in wrong storage paths.
Add useInvalidateSandboxFiles hook and call it alongside
fetchInspectVars/invalidAllLastRun so the Artifacts tab refreshes
automatically when a chatflow preview or workflow debug run finishes.
Move the Divider into the OnlineUsers component so it conditionally
renders together with the online users content, preventing an orphaned
divider from appearing next to the preview button.
Follow-up to SSR prefetch migration (2833965). Eliminates the Zustand
middleman that was syncing TanStack Query data into a separate store.
- Remove useGlobalPublicStore Zustand store entirely
- Create hooks/use-global-public.ts with useSystemFeatures,
useSystemFeaturesQuery, useIsSystemFeaturesPending, useSetupStatusQuery
- Migrate all 93 consumers to import from @/hooks/use-global-public
- Simplify global-public-context.tsx to a thin provider component
- Update 18 test files to mock the new hook interface
- Fix SetupStatusResponse.setup_at type from Date to string (JSON)
- Fix setup-status.spec.ts mock target to match consoleClient
BREAKING CHANGE: useGlobalPublicStore is removed. Use useSystemFeatures()
from @/hooks/use-global-public instead.
Replace direct localStorage.getItem/setItem/removeItem with the
centralized storage module which provides versioned keys, automatic
JSON serialization, SSR safety, and error handling.
Add import skill modal that accepts .zip files via drag-and-drop or
file picker, extracts them client-side using fflate, validates structure
and security constraints, then batch uploads via presigned URLs.
- Add fflate dependency for browser-side ZIP decompression
- Create zip-extract.ts with fflate filter API for validation
- Create zip-to-upload-tree.ts for BatchUploadNodeInput tree building
- Create import-skill-modal.tsx with drag-and-drop support
- Lazy-load ImportSkillModal via next/dynamic for bundle optimization
- Add en-US and zh-Hans i18n keys for import modal
Drop CategoryTabs component, SkillTemplateTag type, icon/tags fields,
and UI_CONFIG from the fetch script. Upload folders now use the
kebab-case skill id (e.g. "skill-creator") instead of the display name.
Card shows the human-readable name from SKILL.md frontmatter while the
created folder uses the id for consistent naming.
Expose initialFocus prop on Modal component (passthrough to Headless
UI Dialog) so the create blank skill modal reliably focuses the name
input when opened, replacing the ineffective autoFocus attribute.
Wire up the "Create Blank Skill" action card to open a modal where
users enter a skill name. The modal validates against existing skill
names in real-time and creates a folder with a SKILL.md file via
batchUpload, then opens the file as a pinned tab.
Add useExistingSkillNames hook that derives root folder names from the
cached asset tree via TanStack Query select, then use it to show an
"Added" state on hover for already-present skills and block re-upload.
Pass disabled/loading props to TemplateCard declaratively from
loadingId state. All cards are disabled while any upload is in
progress, and the active card shows a loading spinner. Remove the
imperative pointer-events overlay in favor of native button disabled.
Replace custom search input with SearchInput component (built-in clear
button) and add 300ms debounce. Fix template card: use Tailwind token
for icon background, fix Badge to use children with badge-s class and
uppercase, match empty-tag fallback height to badge size.
Remove unused categories (Search, Security, Analysis) and add
real ones (Document, Design, Creative). Consolidate xlsx tags to
Document/Productivity and webapp-testing to Development only,
eliminating orphan tags with single-skill coverage.
Use useRef for batchUpload and emitTreeUpdate to remove unstable
dependencies from useCallback, preventing unnecessary memo invalidation
on all 16 TemplateCard components. Wrap filtered list in useMemo and
replace && conditional with ternary for rendering safety.
Add fetch-skill-templates.ts script that clones anthropics/skills repo
and generates complete directory trees (scripts, references, assets)
for all 16 skills with base64 encoding for binary files, replacing
the previous single-SKILL.md-only approach. Generated files are
lazy-loaded per skill on user click.
Introduce type definitions separating raw skill data (SkillTemplate)
from UI metadata (SkillTemplateWithMetadata) to match the actual
skill format from upstream repos. Add template card component with
hover state and file count display, template-to-upload conversion
utility, and i18n keys for en-US/zh-Hans.
Only enable the tool query matching the node's provider_type instead of
all four, and use primitive useMemo dependencies instead of the whole
data object to avoid redundant recomputations on every render.
Add `enabled` parameter to tool query hooks so non-plugin nodes
(LLM, Code, IfElse, etc.) avoid registering React Query observers.
Extract shared matching functions into plugin-install-check utils to
eliminate duplicate logic between the hook and the checklist.
Replace useEffect-based state sync (_pluginInstallLocked/_dimmed) with
render-time derived computation in BaseNode, breaking the cycle of
effect → node data update → re-render → effect. Extract plugin missing
check into a pure utility function for checklist reuse.
Migrate all icon usage in the skill directory from @remixicon/react
and custom SVG components to Tailwind CSS icon classes (i-ri-*, i-custom-*).
Update MenuItem API to accept string class names instead of React.ElementType.
The toolbar download button now uses the already-fetched download URL
from useQuery (zero extra requests), while tree node downloads keep
using useMutation with React Query-managed isPending state instead of
a hand-rolled useState wrapper.
Introduce a dedicated Zustand ArtifactSlice to manage artifact selection
state with mutual exclusion against the main file tree. Artifact files
from the sandbox can now be opened as tabs in the skill editor, rendered
via a lightweight ArtifactContentPanel that reuses ReadOnlyFilePreview.
Backend returns extensions with a leading dot (e.g., `.png` from
`os.path.splitext`), causing binary/media files to be misclassified
as text since they didn't match the dot-free extension lists.
Implement ReadOnlyFilePreview to render sandbox files by type
(code, markdown, image, video, SQLite, unsupported) using existing
skill viewer components with readOnly support. Add
useSandboxFileDownloadUrl and useFetchTextContent hooks for data
fetching, and generalize useFileTypeInfo to accept any file-like
object.
Extract shared empty state card into ArtifactsEmpty component to
deduplicate the no-files and no-selection empty states. Align the
split-panel right-side empty state with the variables tab pattern.
Remove FC type annotations in favor of inline parameter types.
Mark the empty state SearchLinesSparkle icon as aria-hidden for screen
readers. Replace array-index keys with cumulative path keys (O(n) vs
O(n²)) to satisfy react/no-array-index-key and improve key stability.
Replace the minimal centered text card in artifacts tab empty state with
a full-height card layout matching the variables tab, including icon
container (SearchLinesSparkle), title, description, and learn more link.
Replace useClickAway + fixed positioning in file tree context menu with
a floating-ui based hook that provides collision detection (flip/shift),
ARIA role="menu", Escape/outside-click dismiss, and scroll dismiss via
passive capture listener with ref-stabilized callback.
Replaces window.open with the downloadUrl helper from utils/download.ts
to trigger proper browser download behavior via <a download> instead of
opening a new tab that may display garbled content.
Features:
- Add refresh button to clear test run history (data, inputs, node highlights)
- Persist workflowRunningData when closing panel (from previous commit)
Code quality improvements:
- Refactor to declarative pattern: effectiveTab derived from state, not set in effects
- Replace && with ternary operators for conditional rendering (Vercel best practices)
- Fix created_by type: change from string to object to match backend API
- Remove `as any` type assertion, use proper type-safe access
- Title now declaratively shows status based on workflowRunningData presence
Files changed:
- use-workflow-interactions.ts: add handleClearWorkflowRunHistory hook
- workflow-preview.tsx: declarative tab state, clear button, type-safe props
- types.ts: fix created_by type definition
- test files: update mock data to match corrected types
Position the clear button relative to the right divider instead of
following the tab labels, ensuring consistent positioning across
different language translations. Also fix tab switching jitter by
setting a fixed header height.
Use shared object reference instead of separate variables to track
upload progress across concurrent Promise.all operations, preventing
progress bar from showing incorrect or regressing values.
Introduce prepareSkillUploadFile utility that wraps markdown file content
in a JSON payload format before uploading. This ensures consistent handling
of skill files across file upload, folder upload, and drag-and-drop operations.
Replace simple uploading/success indicator with a full three-state
tooltip (uploading, success, partial_error) that overlays the DropTip
position. Add upload slice to skill editor store and wire progress
tracking into file/folder upload operations.
- add SplitPanel to share left/right shell and narrow menu handling
- reuse InspectHeaderProps for tab header + actions across tabs
- refactor variables/artifacts tabs to plug into shared split layout
- align right-side header/close behavior and consolidate empty/loading flows
Consolidate duplicated TabHeader + close button layout (8 occurrences) into a single
InspectLayout wrapper. Replace boolean props with children slots for better composition.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Enhance getFileIconType to accept an extension parameter and cover all
13 FileAppearanceTypeEnum types using an O(1) Map lookup. Update all
call sites to pass the API-provided extension for accurate icon display.
Refactor the variable inspect panel into a tabbed layout with Variables
and Artifacts tabs. Extract variable logic into VariablesTab, add new
ArtifactsTab with sandbox file tree selection and preview pane, and
improve accessibility across tree nodes and interactive elements.
Use useRef to store saveFile reference and remove it from useEffect
dependencies to prevent cleanup from re-triggering on reference changes.
Also normalize metadata before comparison when clearing dirty state to
ensure filtered tools match correctly.
Simpler approach: disable the view picker toggle when preview is running,
preventing users from switching views during active runs.
This replaces the previous mounted ref guard approach (commits a0188bd9b5,
b7f1eb9b7b, 8332f0de2b) which added complexity to handle post-unmount
operations. Disabling the toggle is more direct and follows KISS principle.
Changes:
- Add disabled prop to ViewPicker based on isResponding state
- Revert mounted ref guards in use-chat-flow-control.ts
- Revert isMountedRef parameter in use-nodes/edges-interactions-without-sync.ts
- Revert defensive type check in markdown-utils.ts (no longer needed)
In React StrictMode (dev mode), effects are run twice to help detect
side effects. The cleanup-only pattern left isMountedRef as false after
StrictMode's simulated unmount-remount cycle, causing stop/cancel
operations to be skipped even when the component was mounted.
Now the effect setup explicitly sets isMountedRef.current = true,
ensuring correct behavior in both development and production.
When switching from graph view to skill view during an active preview run,
SSE callbacks continue executing and attempt to update ReactFlow node/edge
states. This could cause errors since the component is unmounted.
Add optional `isMountedRef` parameter to `useNodesInteractionsWithoutSync`
and `useEdgesInteractionsWithoutSync` hooks. When provided, operations are
skipped if the component has unmounted, preventing potential errors while
allowing the SSE connection to continue running in the background.
BREAKING CHANGE: `useNodesInteractionsWithoutSync` and
`useEdgesInteractionsWithoutSync` now accept an optional `isMountedRef`
parameter. Existing callers are unaffected as the parameter is optional.
The sidebar layout was broken when ArtifactsSection expanded - it would
squeeze the FileTree and neither area could scroll. This restructures the
layout so each section has its own scroll container with proper height
constraints.
Remove enabled condition so data is fetched when component mounts,
allowing blue dot to show when files exist even before expanding.
TanStack Query handles request deduplication automatically.
- Replace placeholder with functional ArtifactsSection component
- Add ArtifactsTree component for file tree rendering
- Support expand/collapse with lazy loading
- Show blue dot indicator when collapsed with files
- Add empty state card with hint text
- Add download button on file hover
- Add i18n translations (en-US, zh-Hans)
Cover Ctrl+S and Cmd+S save triggers, guard clauses for start tab and
null active tab, success/error toast notifications, and fallback
registry integration.
Move global keyboard shortcut handling from component-level hook to
SkillSaveProvider, eliminating duplicate event listener registrations
and race conditions. Delete use-skill-file-save hook as its logic is
now consolidated in the provider with direct store access.
- use cleanup-based save on tab switch with stable fallback snapshots
- add fallback registry for metadata-only autosave consistency
- add autosave/save-manager tests
Centralize file save operations using Context/Provider pattern for better
maintainability. Add auto-save on tab switch, visibility change, page unload,
and component unmount.
Refactor StartTabContent into separate components following Figma design specs:
- ActionCard: reusable card with icon, title, description
- SectionHeader: title/xl-semi-bold header with description
- CreateImportSection: 3-column grid layout for Create/Import cards
- SkillTemplatesSection: templates area with placeholder
Align styles with Figma: 3-col grid, 16px title, proper spacing and padding.
Add i18n translations for all user-facing text (en-US, zh-Hans).
- Add guards in tool-block component to skip metadata read/write when Start tab is active
- Add guard in tool-picker-block to prevent writing tool config to Start tab
- Add guard in use-sync-tree-with-active-tab to skip tree sync for Start tab
- Add START_TAB_ID constant and StartTabItem/StartTabContent components
- Default to Start tab when no file tabs are open
- Optimize zustand selectors to subscribe to specific Map values instead of
entire Map objects, reducing unnecessary re-renders when other tabs change
- Refactor useSkillFileSave to accept precise values instead of Map/Set
Display a notice at the bottom of SQLite table preview when data
is truncated due to PREVIEW_ROW_LIMIT (1000 rows), informing users
that additional rows are not displayed.
Optimize folder upload by creating folders at the same depth level in
parallel and uploading all files concurrently. This reduces upload time
from O(n) sequential requests to O(depth) folder requests + 1 file request.
Add overflow-hidden to SkillPageLayout and min-w-0 to flex children
to ensure wide content (like SQLite tables with many columns) scrolls
internally rather than causing the entire page to scroll horizontally.
- Add min-w-0 to flex containers for proper text truncation
- Use w-max on table to ensure columns don't collapse
- Simplify table selector when only one table exists (remove dropdown)
Switch from whitelist to blacklist pattern for determining editable files.
Files are now editable unless they are known binary types (audio, archives,
executables, Office documents, fonts, etc.), enabling support for any
runtime-generated text files without needing to add extensions one by one.
- Convert clickable div to semantic button in artifacts-section
- Add aria-hidden to decorative icons
- Add aria-label to rename inputs and hidden file inputs
- Add i18n keys for artifacts section and rename labels
- Support ignore file extensions (.gitignore, etc.)
Replace event-based auto-expand trigger with Zustand state-driven
approach. Now both external file uploads and internal node drag use
the same isDragOver state as the single source of truth for folder
auto-expand timing (1s blink, 2s expand).
Simplify file drop hooks by removing the unnecessary useUnifiedDrag
wrapper that became redundant after internal node drag was migrated
to react-arborist's built-in system. Now useFolderFileDrop and
useRootFileDrop directly use useFileDrop, reducing code complexity
and eliminating unused treeChildren prop drilling.
Switch from native HTML5 drag to react-arborist's built-in drag system
for internal node drag-and-drop. The HTML5Backend used by react-arborist
was intercepting dragstart events, preventing native drag from working.
- Add onMove callback and disableDrop validation to Tree component
- Sync react-arborist drag state (isDragging, willReceiveDrop) to Zustand
- Simplify use-node-move to only handle API execution
- Update use-unified-drag to only handle external file uploads
- External file drops continue to work via native HTML5 events
Implement unified drag system that supports both internal node moves
and external file uploads with consistent UI feedback. Uses native
HTML5 drag API with shared visual states (isDragOver, isBlinking,
DragActionTooltip showing 'Move to' or 'Upload to').
Move searchTerm from props drilling to zustand store for cleaner
architecture. Remove unnecessary controlled/uncontrolled pattern
and unused debounce logic since search is pure frontend filtering.
- Add fileTreeSearchTerm state to file-tree-slice
- Remove useState and props from main.tsx
- Simplify sidebar-search-add.tsx to read/write store directly
- Add empty state UI with reset filter button