Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion web/src/app/components/ui/AgentToolsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ export function AgentToolsPanel({
<div class="gsv-tools-bar-actions">
<Button
variant="secondary"
label="ADD OVERRIDE"
label="+ ADD OVERRIDE"
disabled={disabled}
onClick={() => onChange(addRule(policy))}
/>
Expand Down
63 changes: 30 additions & 33 deletions web/src/app/components/ui/IconMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { useEffect, useRef } from "preact/hooks";
import { Icon } from "./Icon";
import { Icon, type DotIconMatrix } from "./Icon";
import "./IconMenu.css";

export interface IconMenuCell {
/** Icon name (gsv family, or a doticons/… path with `dotMatrix`). */
icon: string;
label: string;
onClick?: () => void;
/** Highlighted cell (the brighter purple used by the old SETTINGS cell). */
accent?: boolean;
dotMatrix?: DotIconMatrix;
}

export interface IconMenuProps {
title?: string;
/** Popover width in px (280–480). */
Expand All @@ -10,25 +20,17 @@ export interface IconMenuProps {
* menu doesn't steal focus from another control just by being shown. */
autoFocus?: boolean;
onClose?: () => void;
onFiles?: () => void;
onRepositories?: () => void;
onLibrary?: () => void;
onTerminal?: () => void;
onSettings?: () => void;
cells: IconMenuCell[];
}

/** IconMenu — ported from IconMenu.dc.html. GSV control popover: a header bar
* with a pulsing live dot + title + close affordance, over a grid of GSV controls. */
* with a pulsing live dot + title + close affordance, over a grid of cells. */
export function IconMenu({
title = "GSV // CONTROL",
width = 386,
autoFocus = true,
onClose,
onFiles,
onRepositories,
onLibrary,
onTerminal,
onSettings,
cells,
}: IconMenuProps) {
const rootRef = useRef<HTMLDivElement>(null);

Expand Down Expand Up @@ -105,32 +107,27 @@ export function IconMenu({
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(5,minmax(0,1fr))",
gridTemplateColumns: `repeat(${Math.max(cells.length, 1)},minmax(0,1fr))`,
gap: "1px",
background: "var(--rule-inner)",
padding: "1px",
}}
>
<button type="button" disabled={!onFiles} onClick={onFiles} class="gsv-im-cell" style={{ color: "var(--accent-bright)" }}>
<Icon name="folder" size={22} />
<span class="gsv-sublabel" style={{ letterSpacing: ".16em", color: "#7d78b8" }}>FILES</span>
</button>
<button type="button" disabled={!onLibrary} onClick={onLibrary} class="gsv-im-cell" style={{ color: "var(--accent-bright)" }}>
<Icon name="pencil" size={22} />
<span class="gsv-sublabel" style={{ letterSpacing: ".16em", color: "#7d78b8" }}>LIBRARY</span>
</button>
<button type="button" disabled={!onTerminal} onClick={onTerminal} class="gsv-im-cell" style={{ color: "var(--accent-bright)" }}>
<Icon name="terminal" size={22} />
<span class="gsv-sublabel" style={{ letterSpacing: ".16em", color: "#7d78b8" }}>TERMINAL</span>
</button>
<button type="button" disabled={!onRepositories} onClick={onRepositories} class="gsv-im-cell" style={{ color: "var(--accent-bright)" }}>
<Icon name="doticons/branch" size={22} dotMatrix={16} />
<span class="gsv-sublabel" style={{ letterSpacing: ".16em", color: "#7d78b8" }}>REPOS</span>
</button>
<button type="button" disabled={!onSettings} onClick={onSettings} class="gsv-im-cell" style={{ color: "#b6b1ff" }}>
<Icon name="cog" size={22} />
<span class="gsv-sublabel" style={{ letterSpacing: ".16em", color: "#b6b1ff" }}>SETTINGS</span>
</button>
{cells.map((cell) => (
<button
key={cell.label}
type="button"
disabled={!cell.onClick}
onClick={cell.onClick}
class="gsv-im-cell"
style={{ color: cell.accent ? "#b6b1ff" : "var(--accent-bright)" }}
>
<Icon name={cell.icon} size={22} dotMatrix={cell.dotMatrix} />
<span class="gsv-sublabel" style={{ letterSpacing: ".16em", color: cell.accent ? "#b6b1ff" : "#7d78b8" }}>
{cell.label}
</span>
</button>
))}
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion web/src/app/features/chat/components/ChatAgentPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export function ChatAgentPanel({
onChange={setQuery}
/>
<Button
label="NEW TASK"
label="+ NEW TASK"
disabled={!canStartNewTask}
onClick={() => {
onStartNewTask();
Expand Down
4 changes: 2 additions & 2 deletions web/src/app/features/files/components/FilesSurfaceSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ import { useFilesMutations, useFilesPath, useFilesSearch, useFilesTargets } from
import "./FilesSurfaceSummary.css";

/** Sentinel appended to the machine Select so the user can jump to provisioning. */
const ADD_MACHINE_OPTION = "+ ADD MACHINE";
const ADD_MACHINE_OPTION = "+ CONNECT NEW MACHINE";

/** Navigate to the machine-provisioning route. The shell has no nav context that
* reaches this deeply-nested console page, so we push the route + dispatch a
Expand Down Expand Up @@ -921,7 +921,7 @@ export function FilesSurfaceSummary() {
tabs.some((tab) => draftDirty(tab)) ||
(createState.open && (createState.pathInput.trim() !== "" || createState.content !== "")),
);
// "+ ADD MACHINE" leaves the Files surface for the provisioning route, so
// "+ CONNECT NEW MACHINE" leaves the Files surface for the provisioning route, so
// route it through the guard to prompt before discarding a dirty draft.
const requestLeave = useUnsavedGuardLeave();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export function CardListTemplateMockPage({ onOpenChat }: { onOpenChat?: () => vo
listMeta={listMeta}
emptyObject={listTitle}
isEmpty={cards.length === 0}
connectLabel={isCrew ? "NEW AGENT" : "CONNECT MESSENGER"}
connectLabel={isCrew ? "+ NEW AGENT" : "CONNECT MESSENGER"}
onConnect={onOpenChat}
search={showSearch ? { value: search, placeholder: "Search…", onChange: setSearch } : undefined}
filters={showFilter ? (
Expand Down
41 changes: 29 additions & 12 deletions web/src/app/features/gsv-console/components/GsvConsole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ function surfaceTail(surface: ShellSurfaceId): string {
return "GSV · CONSOLE";
}
if (surface === "runtime") {
return "GSV · RUNTIME";
return "GSV · TASKS";
}
if (surface === "models") {
return "GSV · MODELS";
}
if (surface === "messengers") {
return "GSV · MESSENGERS";
Expand Down Expand Up @@ -106,7 +109,7 @@ function listKindForSurface(surface: SettingsListSurface): ConsoleListKind {

function settingsRouteLabel(route: SettingsRoute): string {
if (route.view === "overview") {
return "SETTINGS";
return "OVERVIEW";
}
if (route.view === "crew") {
return "CREW";
Expand Down Expand Up @@ -169,7 +172,7 @@ function libraryPageName(path: string): string {

function settingsRouteTail(route: SettingsRoute): string {
if (route.view === "overview") {
return "GSV · CONTROL";
return "GSV · OVERVIEW";
}
if (route.view === "crew" || route.view === "agent") {
return "GSV · CREW";
Expand Down Expand Up @@ -304,7 +307,7 @@ export function GsvConsole({
);
}
if (isPackageSettingsKind(kind)) {
return <PackageListPage {...options} kind={kind} onOpenApp={onOpenApp} />;
return <PackageListPage {...options} kind={kind} onOpenApp={onOpenApp} onOpenSurface={onOpenSurface} />;
}
return null;
};
Expand All @@ -313,20 +316,22 @@ export function GsvConsole({
navigateSettingsRoute({ view: "overview" });
return;
}
if (surface === "model-default") {
navigateSettingsRoute({ view: "config", kind: "models", select: "default" });
// Crew, models, and tasks are promoted nav surfaces — the overview links
// open the top-level pages rather than the legacy /settings/* nesting.
if (surface === "models" || surface === "model-default") {
onOpenSurface?.("models");
return;
}
if (surface === "models" || surface === "overrides") {
navigateSettingsRoute({ view: "config", kind: surface });
if (surface === "overrides") {
navigateSettingsRoute({ view: "config", kind: "overrides" });
return;
}
if (surface === "crew") {
navigateSettingsRoute({ view: "crew" });
onOpenSurface?.("crew");
return;
}
if (surface === "tasks") {
navigateSettingsRoute({ view: "list", kind: "tasks" });
onOpenSurface?.("runtime");
return;
}
if (surface === "new-agent") {
Expand Down Expand Up @@ -363,7 +368,7 @@ export function GsvConsole({
? [
{ label: "GSV", onClick: onBackToDesktop, notLast: true },
{
label: "SETTINGS",
label: "OVERVIEW",
onClick: inNestedSettings ? () => guardedSettingsNavigate({ view: "overview" }) : undefined,
notLast: inNestedSettings,
},
Expand Down Expand Up @@ -399,6 +404,14 @@ export function GsvConsole({
{ label: "CREW", onClick: backToCrew, notLast: true },
{ label: shellSurfaceLabel(activeSurface) },
]
: activeSurface === "models" && settingsConfigDetail
? [
// Standalone models surface with an open detail: GSV → MODELS → [detail],
// same shape as the settings config trail.
{ label: "GSV", onClick: onBackToDesktop, notLast: true },
{ label: shellSurfaceLabel("models"), onClick: settingsConfigDetail.onExit, notLast: true },
{ label: settingsConfigDetail.label },
]
: activeSurface === "library" && libraryDetail
? [
// Top-level Library: GSV → LIBRARY (→ index) → [page/view].
Expand Down Expand Up @@ -440,6 +453,8 @@ export function GsvConsole({
? goLibraryIndex
: activeSurface === "agent"
? backToCrew
: activeSurface === "models" && settingsConfigDetail
? settingsConfigDetail.onExit
: surfaceDetail
? clearSurfaceDetail
: onBackToDesktop;
Expand Down Expand Up @@ -492,6 +507,8 @@ export function GsvConsole({
)
) : activeSurface === "runtime" ? (
<RuntimePage key={surfaceDetailSeq} onNewTask={onNewTask ?? onOpenChat} onSelectionChange={setSurfaceDetail} />
) : activeSurface === "models" ? (
<ConsoleConfigPage kind="models" onDetailChange={setSettingsConfigDetail} />
) : activeSurface === "crew" ? (
<ConsoleCrewPage onManageAgent={openAgent} onCreateAgent={openNewAgent} />
) : activeSurface === "agent" ? (
Expand All @@ -508,7 +525,7 @@ export function GsvConsole({
) : activeSurface === "integrations" ? (
<IntegrationsPage key={surfaceDetailSeq} onSelectionChange={setSurfaceDetail} />
) : activeSurface === "applications" ? (
<PackageListPage key={surfaceDetailSeq} kind="applications" onOpenApp={onOpenApp} onSelectionChange={setSurfaceDetail} />
<PackageListPage key={surfaceDetailSeq} kind="applications" onOpenApp={onOpenApp} onOpenSurface={onOpenSurface} onSelectionChange={setSurfaceDetail} />
) : activeSurface === "library" ? (
<LibraryPage
route={libraryRoute}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Tag } from "../../../components/ui/Tag";
import { TextInput } from "../../../components/ui/TextInput";
import type { ConnectFlowDef } from "./connectFlowTypes";

// Static mock of the real 2-step "Add application" import → review → enable
// Static mock of the real 2-step "Connect application" import → review → enable
// wizard (see packages/ApplicationImportFlow.tsx). No hooks/mutations — the
// footer buttons just drive the shared connect-flow stepper. Running example:
// a "Weather" web-ui package from team/weather.
Expand All @@ -15,14 +15,14 @@ export const applicationConnectFlow: ConnectFlowDef = {
navLabel: "APPLICATIONS",
parentLabel: "APPLICATIONS",
icon: "satellite",
title: "Add application",
title: "Connect application",
blurb:
"Import a package from a public repo, review it, then enable it · git-clone, agent review, install.",
steps: [
{
key: "import",
label: "IMPORT",
title: "IMPORT FROM REPO",
title: "CONNECT FROM REPO",
meta: "STEP 1 / 2",
status: "NOT IMPORTED",
tone: "idle",
Expand Down Expand Up @@ -75,7 +75,7 @@ export const applicationConnectFlow: ConnectFlowDef = {
<div class="gsv-cf-footer">
<Button variant="secondary" label="CANCEL" onClick={nav.onBack} />
<span class="gsv-cf-footer-spacer" />
<Button variant="primary" label="IMPORT APPLICATION" onClick={nav.onNext} />
<Button variant="primary" label="+ CONNECT APPLICATION" onClick={nav.onNext} />
</div>
</>
),
Expand Down Expand Up @@ -123,7 +123,7 @@ export const applicationConnectFlow: ConnectFlowDef = {
<div class="gsv-cf-footer">
<Button variant="secondary" label="BACK" onClick={nav.onBack} />
<span class="gsv-cf-footer-spacer" />
<Button variant="secondary" label="IMPORT WITHOUT ENABLING" />
<Button variant="secondary" label="CONNECT WITHOUT ENABLING" />
<Button variant="primary" label="APPROVE & ENABLE" />
</div>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ function HeaderFields({
</div>
))}
<div class="gsv-integration-header-actions">
<Button variant="secondary" label="ADD HEADER" onClick={onAdd} />
<Button variant="secondary" label="+ ADD HEADER" onClick={onAdd} />
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function IntegrationsConsoleSection({
listMeta={refreshing ? "REFRESHING" : `${ready.length}/${servers.length} READY`}
emptyObject="INTEGRATIONS"
rows={rows}
connectLabel="NEW INTEGRATION"
connectLabel="+ CONNECT NEW INTEGRATION"
onConnect={onOpenCreate}
search={{ value: query, placeholder: "Search integrations…", onChange: setQuery }}
/>
Expand Down
2 changes: 1 addition & 1 deletion web/src/app/features/gsv-console/library/LibraryPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ function LibraryCollectionBar({ library }: { library: LibraryRuntime }) {
<div class="gsv-library-collection-actions">
<Button
variant="primary"
label={library.createCollectionOpen ? "CLOSE" : "NEW COLLECTION"}
label={library.createCollectionOpen ? "CLOSE" : "+ NEW COLLECTION"}
onClick={() => library.createCollectionOpen
? library.closeCreateCollection()
: library.setCreateCollectionOpen(true)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const MOCK_SURFACES: readonly MockSurface[] = [
key: "machines",
listTitle: "MACHINES",
emptyObject: "MACHINES",
connectLabel: "CONNECT NEW MACHINE",
connectLabel: "+ CONNECT NEW MACHINE",
connect: { kind: "section", section: "machines" },
state: "ONLINE",
rows: [
Expand All @@ -40,7 +40,7 @@ const MOCK_SURFACES: readonly MockSurface[] = [
key: "integrations",
listTitle: "INTEGRATIONS",
emptyObject: "INTEGRATIONS",
connectLabel: "NEW INTEGRATION",
connectLabel: "+ CONNECT NEW INTEGRATION",
connect: { kind: "section", section: "integrations" },
state: "READY",
rows: [
Expand All @@ -53,7 +53,7 @@ const MOCK_SURFACES: readonly MockSurface[] = [
key: "tasks",
listTitle: "TASKS",
emptyObject: "TASKS",
connectLabel: "NEW TASK",
connectLabel: "+ NEW TASK",
connect: { kind: "chat" },
state: "ACTIVE",
rows: [
Expand Down
2 changes: 1 addition & 1 deletion web/src/app/features/gsv-console/machines/MachinesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function MachinesConsoleSection({
listMeta={refreshing ? "REFRESHING" : `${onlineCount}/${targets.length} ONLINE`}
emptyObject="MACHINES"
rows={rows}
connectLabel="CONNECT NEW MACHINE"
connectLabel="+ CONNECT NEW MACHINE"
onConnect={onOpenCreate}
search={{ value: query, placeholder: "Search machines…", onChange: setQuery }}
/>
Expand Down
Loading
Loading