Skip to content

Commit a1fa454

Browse files
os-zhuangCopilot
andcommitted
feat(studio): developer UX overhaul — Inspector, Problems, Hotkeys, Actions, Onboarding
Add cross-cutting tools to make Studio feel like a real developer workbench: * Inspector drawer (right Sheet, toggle via header button or `]`) - API tab: endpoint, sample curl GET/POST - Source tab: raw metadata JSON with copy - Refs tab: cross-references (objects → views/hooks/flows) - Auto-populates when navigating to a resource detail page * Problems panel (status bar pill + slide-up panel, `[`) - Subscribes to object/view/flow/hook changes - Reports unknown object refs, missing field refs, broken triggers - Deep-links problems back to source resource * Keyboard shortcuts - `g o` / `g f` / `g v` / `g a` / `g s` / `g p` nav - `[` problems, `]` inspector, `?` help dialog * Resource actions menu (⋯ on detail page header) - Copy as curl / fetch() / defineX() TypeScript / Metadata JSON - Open in VS Code, Open API endpoint * Welcome onboarding (empty-state in DeveloperOverview) - 3 CTA cards: Create object / Playground / Browse docs * StudioShell wraps all of the above; TopBar grows `rightSlot` for Inspector/Help buttons Also: surface RECORD_LOCKED-style plain-string `error` bodies in client fetch error messages instead of swallowing them as "Bad Request". Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 03e6e61 commit a1fa454

19 files changed

Lines changed: 1468 additions & 45 deletions

apps/studio/src/components/DeveloperOverview.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
RefreshCw, ExternalLink, Code2
1212
} from 'lucide-react';
1313
import type { InstalledPackage } from '@objectstack/spec/kernel';
14+
import { WelcomeOnboarding } from '@/components/WelcomeOnboarding';
1415

1516
interface DeveloperOverviewProps {
1617
packages: InstalledPackage[];
@@ -83,6 +84,11 @@ export function DeveloperOverview({ packages, selectedPackage, onNavigate = () =
8384
const objectCount = stats.metadata.counts['object'] || 0;
8485
const totalMetaItems = Object.values(stats.metadata.counts).reduce((a, b) => a + b, 0);
8586

87+
// First-run onboarding: no metadata authored yet for this package.
88+
if (!stats.loading && totalMetaItems === 0 && selectedPackage?.manifest?.id) {
89+
return <WelcomeOnboarding packageId={selectedPackage.manifest.id} />;
90+
}
91+
8692
return (
8793
<div className="flex flex-1 flex-col gap-6 p-6 overflow-auto">
8894
{/* Header */}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* Keyboard shortcuts help dialog. Triggered by `?` or via the command
5+
* palette. Lists every shortcut registered in `useStudioHotkeys`.
6+
*/
7+
8+
import {
9+
Dialog,
10+
DialogContent,
11+
DialogDescription,
12+
DialogHeader,
13+
DialogTitle,
14+
} from '@/components/ui/dialog';
15+
import { STUDIO_SHORTCUTS } from '@/hooks/useStudioHotkeys';
16+
17+
interface HotkeysHelpDialogProps {
18+
open: boolean;
19+
onOpenChange: (v: boolean) => void;
20+
}
21+
22+
export function HotkeysHelpDialog({ open, onOpenChange }: HotkeysHelpDialogProps) {
23+
const groups = new Map<string, typeof STUDIO_SHORTCUTS>();
24+
for (const s of STUDIO_SHORTCUTS) {
25+
if (!groups.has(s.group)) groups.set(s.group, []);
26+
(groups.get(s.group) as any).push(s);
27+
}
28+
return (
29+
<Dialog open={open} onOpenChange={onOpenChange}>
30+
<DialogContent className="sm:max-w-md">
31+
<DialogHeader>
32+
<DialogTitle>Keyboard shortcuts</DialogTitle>
33+
<DialogDescription>
34+
Most shortcuts use the <kbd className="px-1 rounded bg-muted text-xs">g</kbd>{' '}
35+
prefix for "go to". Keys are ignored while typing.
36+
</DialogDescription>
37+
</DialogHeader>
38+
<div className="space-y-4 max-h-[60vh] overflow-auto">
39+
{[...groups.entries()].map(([group, items]) => (
40+
<div key={group}>
41+
<div className="text-[11px] uppercase tracking-wide text-muted-foreground mb-1">
42+
{group}
43+
</div>
44+
<ul className="space-y-1">
45+
{items.map((s) => (
46+
<li
47+
key={s.keys + s.label}
48+
className="flex items-center justify-between text-sm py-1"
49+
>
50+
<span>{s.label}</span>
51+
<kbd className="px-1.5 py-0.5 rounded bg-muted text-xs font-mono">
52+
{s.keys}
53+
</kbd>
54+
</li>
55+
))}
56+
</ul>
57+
</div>
58+
))}
59+
</div>
60+
</DialogContent>
61+
</Dialog>
62+
);
63+
}

0 commit comments

Comments
 (0)