('files/homeRoot', 'home');
- const viewPath = searchParams.get('view');
- const fileName = viewPath ? viewPath.split('/').pop()! : '';
-
- const handleOpenFile = useCallback(
- (filePath: string, root: string) => {
- setSearchParams((prev) => {
- const next = new URLSearchParams(prev);
- next.set('ephemeral', filePath);
- next.set('ephemeralRoot', root);
- return next;
- });
- },
- [setSearchParams],
- );
-
- if (!viewPath) return null;
-
- return (
-
- {children}
-
- );
-};
-
-const EphemeralFileViewerProvider = ({ children }: { children: ReactNode }) => {
- const [searchParams, setSearchParams] = useSearchParams();
- const ephemeralPath = searchParams.get('ephemeral');
- const ephemeralRoot = searchParams.get('ephemeralRoot') ?? 'home';
- const fileName = ephemeralPath ? ephemeralPath.split('/').pop()! : '';
-
- const handleOpenFile = useCallback(
- (filePath: string, root: string) => {
- setSearchParams((prev) => {
- const next = new URLSearchParams(prev);
- next.set('ephemeral2', filePath);
- next.set('ephemeral2Root', root);
- next.set('ephemeral2Auto', '1');
- return next;
- });
- },
- [setSearchParams],
- );
-
- if (!ephemeralPath) return null;
-
- return (
-
- {children}
-
- );
-};
-
-const Ephemeral2FileViewerProvider = ({ children }: { children: ReactNode }) => {
- const [searchParams] = useSearchParams();
- const path = searchParams.get('ephemeral2');
- const root = searchParams.get('ephemeral2Root') ?? 'home';
- const autoPlay = searchParams.get('ephemeral2Auto') === '1';
- const fileName = path ? path.split('/').pop()! : '';
-
- if (!path) return null;
-
- return (
-
- {children}
-
- );
-};
-
-export const FilesPage = () => {
- const [searchParams, setSearchParams] = useSearchParams();
- const viewPath = searchParams.get('view');
- const ephemeralPath = searchParams.get('ephemeral');
- const ephemeral2Path = searchParams.get('ephemeral2');
-
- const layout = useMemo(
- () =>
- viewPath && ephemeralPath && ephemeral2Path
- ? viewerWithEphemeralSplitLayout
- : viewPath && ephemeralPath
- ? viewerWithEphemeralLayout
- : viewPath
- ? viewerLayout
- : baseLayout,
- [viewPath, ephemeralPath, ephemeral2Path],
- );
-
- const closeEphemeral = useCallback(() => {
- setSearchParams((prev) => {
- const next = new URLSearchParams(prev);
- next.delete('ephemeral');
- next.delete('ephemeralRoot');
- next.delete('ephemeral2');
- next.delete('ephemeral2Root');
- next.delete('ephemeral2Auto');
- return next;
- });
- }, [setSearchParams]);
-
- const closeEphemeral2 = useCallback(() => {
- setSearchParams((prev) => {
- const next = new URLSearchParams(prev);
- next.delete('ephemeral2');
- next.delete('ephemeral2Root');
- next.delete('ephemeral2Auto');
- return next;
- });
- }, [setSearchParams]);
-
- const panelComponents: PanelComponents = useMemo(
- () => ({
- 'files-main': FilesInner,
- 'files-viewer': {
- provider: FilesFileViewerProvider,
- header: FileViewerHeader,
- component: FileViewerBody,
- onClose: () => setSearchParams({}),
- },
- 'files-ephemeral': {
- provider: EphemeralFileViewerProvider,
- header: FileViewerHeader,
- component: FileViewerBody,
- onClose: closeEphemeral,
- },
- 'files-ephemeral2': {
- provider: Ephemeral2FileViewerProvider,
- header: FileViewerHeader,
- component: FileViewerBody,
- onClose: closeEphemeral2,
- },
- }),
- [setSearchParams, closeEphemeral, closeEphemeral2],
- );
-
- return (
-
- {}} registry={appRegistry} components={panelComponents} />
-
- );
-};
-
-export { FileBrowser as FileBrowserApp } from './Widget';
+export * from './FilesScreen';
diff --git a/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListScreen.tsx b/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListScreen.tsx
index 2c4bf286..0fa87b41 100644
--- a/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListScreen.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListScreen.tsx
@@ -10,7 +10,7 @@ import { WorkspaceLayout, WorkspaceView, createDefaultLayout } from '@/component
import type { LayoutNode, WorkspaceDefinition } from '@/components/Workspace';
import { Button } from '@/components/ui/button';
import { generateSlug, slugify } from 'helpers/slug';
-import { Files } from '../Files';
+import { Files } from 'apps/FileBrowser';
import { appRegistry } from './app-registry';
import { SELECTED_WORKSPACE_KEY, CREATING_WORKSPACE_KEY, EDITING_WORKSPACE_KEY, NEW_WS_NAME_KEY, NEW_WS_DESC_KEY, NEW_WS_TEMPLATE_KEY } from './constants';
diff --git a/src/apps/officer-web/Screens/Dashboard/Workspaces/app-registry.tsx b/src/apps/officer-web/Screens/Dashboard/Workspaces/app-registry.tsx
index 20167b7f..b56f53dd 100644
--- a/src/apps/officer-web/Screens/Dashboard/Workspaces/app-registry.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Workspaces/app-registry.tsx
@@ -1,6 +1,7 @@
import { useState, useEffect, useRef } from 'react';
import type { ReactNode } from 'react';
-import { MessageSquare, FolderOpen, History, Music, Code, TerminalSquare, Monitor, LayoutGrid, LayoutDashboard, Sparkles, Eye, FolderKanban, Columns2, PenLine } from 'lucide-react';
+import { MessageSquare, FolderOpen, History, Music, Code, TerminalSquare } from 'lucide-react';
+import { Monitor, LayoutGrid, LayoutDashboard, Sparkles, Eye, FolderKanban, Columns2, PenLine } from 'lucide-react';
import { useAuth } from 'hooks/useAuth';
import type { AppRegistry } from '@/components/Workspace';
import { useWorkspace } from '@/components/Workspace';
@@ -12,7 +13,7 @@ import { FileViewerProvider, FileViewerHeader, FileViewerBody } from 'apps/FileV
import { EmbeddableChat, ChatLauncher, usePi } from 'apps/Chat';
import { useVisiblePiModels } from '@/state/useModels';
import { ChatHistoryApp as ChatHistory } from '../ChatHistory';
-import { Files } from '../Files';
+import { Files } from 'apps/FileBrowser';
import { Catalog } from 'sounds';
import { WorkspaceListApp } from './WorkspaceListApp';
import { ProjectListApp } from '../Projects/ProjectListApp';
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileGrid.tsx b/src/workspaces/apps/FileBrowser/FileGrid.tsx
similarity index 99%
rename from src/apps/officer-web/Screens/Dashboard/Files/Screen/FileGrid.tsx
rename to src/workspaces/apps/FileBrowser/FileGrid.tsx
index c18d1b85..2001d8d7 100644
--- a/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileGrid.tsx
+++ b/src/workspaces/apps/FileBrowser/FileGrid.tsx
@@ -1,7 +1,8 @@
import { useRef, useCallback, useMemo, useState, useEffect } from 'react';
import { useVirtualizer } from '@tanstack/react-virtual';
import { ChevronUp, ChevronDown } from 'lucide-react';
-import type { DirEntry, TaskSummary } from 'apps/FileBrowser';
+import type { DirEntry } from './useFiles';
+import type { TaskSummary } from './useTasks';
import { getFileType } from 'apps/FileViewer';
import { FileItem } from './FileItem';
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileItem.tsx b/src/workspaces/apps/FileBrowser/FileItem.tsx
similarity index 99%
rename from src/apps/officer-web/Screens/Dashboard/Files/Screen/FileItem.tsx
rename to src/workspaces/apps/FileBrowser/FileItem.tsx
index 5ae41692..e5c30c7c 100644
--- a/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileItem.tsx
+++ b/src/workspaces/apps/FileBrowser/FileItem.tsx
@@ -22,7 +22,8 @@ import {
ContextMenuTrigger,
} from '@/components/ui/context-menu';
import { cardStyle } from '@/components/Card';
-import type { DirEntry, TaskSummary } from 'apps/FileBrowser';
+import type { DirEntry } from './useFiles';
+import type { TaskSummary } from './useTasks';
import { getFileType } from 'apps/FileViewer';
export type FileItemProps = {
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/Screen/index.tsx b/src/workspaces/apps/FileBrowser/Files.tsx
similarity index 99%
rename from src/apps/officer-web/Screens/Dashboard/Files/Screen/index.tsx
rename to src/workspaces/apps/FileBrowser/Files.tsx
index ed3f3011..c3a6c8d0 100644
--- a/src/apps/officer-web/Screens/Dashboard/Files/Screen/index.tsx
+++ b/src/workspaces/apps/FileBrowser/Files.tsx
@@ -24,7 +24,10 @@ import { getIcon } from 'material-file-icons';
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger } from '@/components/ui/context-menu';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog';
import { Checkbox } from '@/components/ui/checkbox';
-import { useFiles, type DirEntry, useTasks, type TaskSummary, Breadcrumb, Toolbar } from 'apps/FileBrowser';
+import { useFiles, type DirEntry } from './useFiles';
+import { useTasks, type TaskSummary } from './useTasks';
+import { Breadcrumb } from './Breadcrumb';
+import { Toolbar } from './Toolbar';
import { useClient } from 'hooks/useClient';
import { useUserState } from '@/state/useUserState';
import { useAuth } from 'hooks/useAuth';
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/Screen/TaskRunnerModal.tsx b/src/workspaces/apps/FileBrowser/TaskRunnerModal.tsx
similarity index 98%
rename from src/apps/officer-web/Screens/Dashboard/Files/Screen/TaskRunnerModal.tsx
rename to src/workspaces/apps/FileBrowser/TaskRunnerModal.tsx
index 46827833..2739c4fd 100644
--- a/src/apps/officer-web/Screens/Dashboard/Files/Screen/TaskRunnerModal.tsx
+++ b/src/workspaces/apps/FileBrowser/TaskRunnerModal.tsx
@@ -7,7 +7,7 @@ import type { TaskInfo } from 'apps/Chat';
import { usePi, EmbeddableChat } from 'apps/Chat';
import { useVisiblePiModels } from '@/state/useModels';
import { useSettings } from '@/state/useSettings';
-import type { TaskSummary } from 'apps/FileBrowser';
+import type { TaskSummary } from './useTasks';
const playDing = () => {
const ctx = new AudioContext();
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/Widget.tsx b/src/workspaces/apps/FileBrowser/Widget.tsx
similarity index 98%
rename from src/apps/officer-web/Screens/Dashboard/Files/Widget.tsx
rename to src/workspaces/apps/FileBrowser/Widget.tsx
index d2080f5f..bdd06168 100644
--- a/src/apps/officer-web/Screens/Dashboard/Files/Widget.tsx
+++ b/src/workspaces/apps/FileBrowser/Widget.tsx
@@ -2,9 +2,10 @@ import { useState, useEffect, useRef, useMemo } from 'react';
import { useNavigate } from 'react-router';
import { Folder, Pin, PinOff, Search, Clock, FolderOpen, Loader2, X } from 'lucide-react';
import { getIcon } from 'material-file-icons';
-import { useFiles, type DirEntry, Breadcrumb } from 'apps/FileBrowser';
-import { useRecentFiles } from './state/useRecentFiles';
-import { usePinnedFiles } from './state/usePinnedFiles';
+import { useFiles, type DirEntry } from './useFiles';
+import { Breadcrumb } from './Breadcrumb';
+import { useRecentFiles } from './useRecentFiles';
+import { usePinnedFiles } from './usePinnedFiles';
import { Widget } from 'widgets/Widget';
type Tab = 'browse' | 'recent' | 'pinned';
diff --git a/src/workspaces/apps/FileBrowser/index.ts b/src/workspaces/apps/FileBrowser/index.ts
index dfd52576..7e942c59 100644
--- a/src/workspaces/apps/FileBrowser/index.ts
+++ b/src/workspaces/apps/FileBrowser/index.ts
@@ -2,3 +2,10 @@ export { Breadcrumb } from './Breadcrumb';
export { Toolbar } from './Toolbar';
export { useFiles, type DirEntry } from './useFiles';
export { useTasks, type TaskSummary } from './useTasks';
+export { Files } from './Files';
+export { FileBrowser } from './Widget';
+export { FileGrid } from './FileGrid';
+export { FileItem, type FileItemProps } from './FileItem';
+export { TaskRunnerModal } from './TaskRunnerModal';
+export { useRecentFiles } from './useRecentFiles';
+export { usePinnedFiles } from './usePinnedFiles';
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/state/usePinnedFiles.ts b/src/workspaces/apps/FileBrowser/usePinnedFiles.ts
similarity index 100%
rename from src/apps/officer-web/Screens/Dashboard/Files/state/usePinnedFiles.ts
rename to src/workspaces/apps/FileBrowser/usePinnedFiles.ts
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/state/useRecentFiles.ts b/src/workspaces/apps/FileBrowser/useRecentFiles.ts
similarity index 100%
rename from src/apps/officer-web/Screens/Dashboard/Files/state/useRecentFiles.ts
rename to src/workspaces/apps/FileBrowser/useRecentFiles.ts
diff --git a/src/workspaces/officerdev/package.json b/src/workspaces/officerdev/package.json
new file mode 100644
index 00000000..41d3c312
--- /dev/null
+++ b/src/workspaces/officerdev/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "officerdev",
+ "version": "0.0.1",
+ "license": "MIT",
+ "type": "module",
+ "exports": {
+ ".": "./src/index.ts",
+ "./*": "./src/*.ts"
+ }
+}
diff --git a/src/workspaces/officerdev/src/hooks/appRegistry/appRegistry.ts b/src/workspaces/officerdev/src/hooks/appRegistry/appRegistry.ts
new file mode 100644
index 00000000..3a73800e
--- /dev/null
+++ b/src/workspaces/officerdev/src/hooks/appRegistry/appRegistry.ts
@@ -0,0 +1,5 @@
+import type { AppRegistry } from '@/components/Workspace';
+
+export function useAppRegistry(): AppRegistry {
+ return {};
+}
diff --git a/src/workspaces/officerdev/src/hooks/appRegistry/index.ts b/src/workspaces/officerdev/src/hooks/appRegistry/index.ts
new file mode 100644
index 00000000..090ec6fb
--- /dev/null
+++ b/src/workspaces/officerdev/src/hooks/appRegistry/index.ts
@@ -0,0 +1 @@
+export { useAppRegistry } from './appRegistry';
diff --git a/src/workspaces/officerdev/src/hooks/index.ts b/src/workspaces/officerdev/src/hooks/index.ts
new file mode 100644
index 00000000..84d2d029
--- /dev/null
+++ b/src/workspaces/officerdev/src/hooks/index.ts
@@ -0,0 +1 @@
+export * from './appRegistry';
diff --git a/src/workspaces/officerdev/src/index.ts b/src/workspaces/officerdev/src/index.ts
new file mode 100644
index 00000000..4cc90d02
--- /dev/null
+++ b/src/workspaces/officerdev/src/index.ts
@@ -0,0 +1 @@
+export * from './hooks';
diff --git a/src/workspaces/officerdev/src/useAppRegistry.ts b/src/workspaces/officerdev/src/useAppRegistry.ts
new file mode 100644
index 00000000..3a73800e
--- /dev/null
+++ b/src/workspaces/officerdev/src/useAppRegistry.ts
@@ -0,0 +1,5 @@
+import type { AppRegistry } from '@/components/Workspace';
+
+export function useAppRegistry(): AppRegistry {
+ return {};
+}