gitea, extracted into plugins/gitea — the platform half
Eleventh extraction, and the only one that deleted its UI instead of moving it. Seventeen files under apps/Gitea/ reimplemented a fraction of an interface Gitea already has; the plugin renders the real one in an iframe over a same-origin proxy. Deleting rather than moving removed a platform change nobody now has to build: useGiteaLocation read :owner/:name/:tab/:item from five route patterns where a plugin gets two, so an as-is extraction would have required teaching the plugin system to declare extra route patterns. Nothing inside an iframe needs a route. Nothing working was lost. The router had been commented out of hono.ts since 2026-08-13 with api: [], and officer-gitea was running nowhere — the screen rendered and every request 404'd, like soulseek, jellyfin and invoices before it. Gitea also stops being a CORE DOCK ITEM. It was hardcoded in CORE_DOCK_ITEMS, the only installable feature that was, on the reasoning that it fronts a remote instance and installs nothing locally. As a plugin its tile arrives from assets/icon.png like every other — which is also why the icon added to public/icons/ this morning was reverted rather than kept. 'gitea' stays in the ServiceName union: the connection lives in core service_connections, and gitea is the reason that table has its "member rows carry only a token and inherit the owner's URL" shape at all. registry.test.ts's grantable-permission test named gitea as "the case this was built for"; a plugin's permissions are registered at install and are never in that compile-time list, so it now names a core one. catalogue.test.ts re-anchored to vnc/notify. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -102,11 +102,6 @@ export function App() {
|
||||
element={<Dashboard.PluginScreen {...plugin} />}
|
||||
/>,
|
||||
])}
|
||||
<Route path="/gitea" element={<Dashboard.GiteaScreen />} />
|
||||
<Route path="/gitea/:section" element={<Dashboard.GiteaScreen />} />
|
||||
<Route path="/gitea/repo/:owner/:name" element={<Dashboard.GiteaScreen />} />
|
||||
<Route path="/gitea/repo/:owner/:name/:tab" element={<Dashboard.GiteaScreen />} />
|
||||
<Route path="/gitea/repo/:owner/:name/:tab/:item" element={<Dashboard.GiteaScreen />} />
|
||||
<Route path="/system-monitor" element={<Dashboard.SystemMonitorScreen />} />
|
||||
<Route path="/system-monitor/:scope" element={<Dashboard.SystemMonitorScreen />} />
|
||||
{/* TEMPORARY / EXPERIMENTAL — offline file transfer over animated QR codes */}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { Navigate, useParams } from 'react-router';
|
||||
import type { LayoutNode } from 'officerdev';
|
||||
import { WorkspaceView, DEFAULT_GITEA_SECTION, giteaSectionPath, isGiteaSection } from 'officerdev';
|
||||
import { useDashboardState } from 'state/useDashboardState';
|
||||
import { defaultLayout } from './defaultLayout';
|
||||
|
||||
// /gitea uses the Workspace/Panel system (like /command-hq): a section nav on the left and the section view
|
||||
// on the right. Both talk to the officer-gitea sidecar through the /api/gitea auth proxy, which holds no
|
||||
// Gitea credentials of its own — the instance URL and the access token live in the sidecar.
|
||||
//
|
||||
// The open section is :section in the URL, so the panels read it with useParams instead of passing state
|
||||
// between themselves over a channel. This screen backs both /gitea and /gitea/:section and is the single
|
||||
// place that decides what an absent or bogus section means.
|
||||
|
||||
export const GiteaScreen = () => {
|
||||
const { section, owner, name } = useParams();
|
||||
const workspace = useDashboardState<LayoutNode>('screens/gitea', defaultLayout);
|
||||
|
||||
// Bare /gitea, or a section that doesn't exist, resolves to a canonical URL rather than rendering a default
|
||||
// while the address bar says something else — the nav highlight is derived from the URL.
|
||||
//
|
||||
// A repository route (/gitea/repo/:owner/:name/…) has no :section at all and is exempt: it is a second,
|
||||
// deeper shape on the same screen, not a malformed section.
|
||||
const inRepo = !!owner && !!name;
|
||||
if (!inRepo && !isGiteaSection(section)) {
|
||||
return <Navigate to={giteaSectionPath(DEFAULT_GITEA_SECTION)} replace />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full w-full pt-2">
|
||||
<WorkspaceView
|
||||
workspace={workspace}
|
||||
locked
|
||||
appTypes={{ allowed: ['gitea-nav', 'gitea-view'], fallback: 'gitea-view' }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { LayoutNode } from 'officerdev';
|
||||
|
||||
export const defaultLayout: LayoutNode = {
|
||||
type: 'group',
|
||||
id: 'gitea-root',
|
||||
direction: 'horizontal',
|
||||
children: [
|
||||
{ node: { type: 'panel', id: 'gitea-nav', appType: 'gitea-nav' }, size: 22 },
|
||||
{ node: { type: 'panel', id: 'gitea-view', appType: 'gitea-view' }, size: 78 },
|
||||
],
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './GiteaScreen';
|
||||
@@ -173,7 +173,6 @@ export const CORE_DOCK_ITEMS: DockItem[] = [
|
||||
{ label: 'Files', to: '/files', icon: FolderOpen, color: '#fbbf24' },
|
||||
{ label: 'Chat', to: '/chat', icon: MessageCircle, color: '#60a5fa' },
|
||||
{ label: 'Command HQ', to: '/command-hq', icon: Radar, color: '#f43f5e' },
|
||||
{ label: 'Gitea', to: '/gitea', icon: GitBranch, color: '#609926' },
|
||||
{ label: 'Editor', to: '/code-editor', icon: Code, color: '#a78bfa' },
|
||||
{ label: 'Jobs', to: '/jobs', icon: Workflow, color: '#14b8a6' },
|
||||
{ label: 'Terminal', to: '/terminal', icon: Monitor, color: '#f97316' },
|
||||
|
||||
@@ -14,7 +14,6 @@ export * from './Files';
|
||||
export * from './Calendar';
|
||||
export * from './Contacts';
|
||||
export * from './CommandHQ';
|
||||
export * from './Gitea';
|
||||
export * from './SystemMonitor';
|
||||
export * from './Activity';
|
||||
export * from './CodeEditor';
|
||||
|
||||
@@ -24,7 +24,6 @@ const RULES: TitleRule[] = [
|
||||
{ match: (p) => p.startsWith('/marketplace'), title: 'Marketplace' },
|
||||
{ match: (p) => p.startsWith('/plugins'), title: 'Marketplace' },
|
||||
{ match: (p) => p.startsWith('/command-hq'), title: 'Command HQ' },
|
||||
{ match: (p) => p.startsWith('/gitea'), title: 'Gitea' },
|
||||
{ match: (p) => p.startsWith('/system-monitor'), title: 'System Monitor' },
|
||||
{ match: (p) => p.startsWith('/qr-transfer'), title: 'QR Transfer' },
|
||||
{ match: (p) => p.startsWith('/activity'), title: 'Activity' },
|
||||
|
||||
Reference in New Issue
Block a user