@korso/shepherd-ui 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/ShepherdRoot.d.ts +6 -1
- package/dist/ShepherdRoot.js +4 -3
- package/dist/components/Chat.js +1 -1
- package/dist/components/Composer.js +1 -1
- package/dist/components/Dashboard.d.ts +6 -1
- package/dist/components/Dashboard.js +2 -2
- package/dist/components/DoneList.js +1 -1
- package/dist/config/ConfigPanel.d.ts +6 -1
- package/dist/config/ConfigPanel.js +3 -2
- package/dist/config/ConfirmDeleteWorkspace.js +1 -1
- package/dist/config/ConnectAgent.js +2 -2
- package/dist/config/SignOutAction.d.ts +7 -0
- package/dist/config/SignOutAction.js +7 -0
- package/dist/lib/{Dashboard-BSGZuM-A.js → Dashboard-DNr0hdbW.js} +604 -561
- package/dist/lib/index.d.ts +18 -3
- package/dist/lib/index.js +390 -371
- package/dist/lib/selfhost.js +32 -32
- package/dist/lib/styles.css +12 -0
- package/dist/selfhost/assets/index-DLumdd7Z.js +40 -0
- package/dist/selfhost/assets/index-Do2kYzi3.css +1 -0
- package/dist/selfhost/index.html +2 -2
- package/dist/selfhost.js +14 -13
- package/package.json +1 -1
- package/dist/selfhost/assets/index-BQL_SpLG.js +0 -40
- package/dist/selfhost/assets/index-CGncP2MD.css +0 -1
package/README.md
CHANGED
|
@@ -32,6 +32,9 @@ image regenerate them.
|
|
|
32
32
|
**only** here, never in the `.` export or `<Dashboard/>`.
|
|
33
33
|
- **Hosted (Korso).** Korso imports `<Dashboard/>` + `createShepherdClient` from
|
|
34
34
|
the `.` export and supplies its own auth layer (the BFF). No token gate.
|
|
35
|
+
Hosted shells that embed `<ShepherdRoot/>` may pass `onLogout`; Shepherd renders
|
|
36
|
+
the Sign out action at the bottom of Config, while the host callback performs
|
|
37
|
+
the actual session cleanup and navigation (cookies, OIDC, BFF state, etc.).
|
|
35
38
|
|
|
36
39
|
## Development
|
|
37
40
|
|
package/dist/ShepherdRoot.d.ts
CHANGED
|
@@ -5,5 +5,10 @@ export interface ShepherdRootProps {
|
|
|
5
5
|
* to the client's baseUrl, which is correct for self-host.
|
|
6
6
|
*/
|
|
7
7
|
hubUrl?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Hosted session logout hook. The embedding frontend/BFF owns cookies,
|
|
10
|
+
* session state, and OIDC cleanup; Shepherd only renders the Config action.
|
|
11
|
+
*/
|
|
12
|
+
onLogout?: () => void;
|
|
8
13
|
}
|
|
9
|
-
export declare function ShepherdRoot({ hubUrl }: ShepherdRootProps): import("react").JSX.Element;
|
|
14
|
+
export declare function ShepherdRoot({ hubUrl, onLogout }: ShepherdRootProps): import("react").JSX.Element;
|
package/dist/ShepherdRoot.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useState } from "react";
|
|
3
3
|
import { Dashboard } from "./components/Dashboard.js";
|
|
4
4
|
import { WorkspaceSwitcher } from "./config/WorkspaceSwitcher.js";
|
|
5
5
|
import { ConfigPanel } from "./config/ConfigPanel.js";
|
|
6
|
+
import { SignOutAction } from "./config/SignOutAction.js";
|
|
6
7
|
import { useShepherdClient } from "./context.js";
|
|
7
8
|
import { describeError } from "./client.js";
|
|
8
|
-
export function ShepherdRoot({ hubUrl }) {
|
|
9
|
+
export function ShepherdRoot({ hubUrl, onLogout }) {
|
|
9
10
|
const client = useShepherdClient();
|
|
10
11
|
const [load, setLoad] = useState({ status: "loading" });
|
|
11
12
|
const [selectedId, setSelectedId] = useState(null);
|
|
@@ -48,6 +49,6 @@ export function ShepherdRoot({ hubUrl }) {
|
|
|
48
49
|
// The Config tab's content. With a workspace selected it's the sidebar
|
|
49
50
|
// <ConfigPanel>; with none it's a prompt pointing at the switcher, since
|
|
50
51
|
// create/join now live there (the sidebar has nothing to configure).
|
|
51
|
-
const configSection = (_jsxs("section", { className: "shepherd-config", "aria-labelledby": "config-heading", children: [_jsx("h2", { id: "config-heading", children: "Configuration" }), selected ? (_jsx(ConfigPanel, { workspace: selected, hubUrl: hubUrl, membersRefreshKey: membersRefreshKey, onMembersChanged: () => setMembersRefreshKey((k) => k + 1), onLeft: () => void fetchWorkspaces(), onDeleted: () => void fetchWorkspaces() })) : (_jsxs("p", { className: "config-none", children: ["You're not in a workspace yet. Use the ", _jsx("b", { children: "Get started \u25BE" }), " menu at the top to create one or join with an invite code."] }))] }));
|
|
52
|
+
const configSection = (_jsxs("section", { className: "shepherd-config", "aria-labelledby": "config-heading", children: [_jsx("h2", { id: "config-heading", children: "Configuration" }), selected ? (_jsx(ConfigPanel, { workspace: selected, hubUrl: hubUrl, membersRefreshKey: membersRefreshKey, onMembersChanged: () => setMembersRefreshKey((k) => k + 1), onLeft: () => void fetchWorkspaces(), onDeleted: () => void fetchWorkspaces(), onLogout: onLogout })) : (_jsxs(_Fragment, { children: [_jsxs("p", { className: "config-none", children: ["You're not in a workspace yet. Use the ", _jsx("b", { children: "Get started \u25BE" }), " menu at the top to create one or join with an invite code."] }), onLogout && _jsx(SignOutAction, { onLogout: onLogout })] }))] }));
|
|
52
53
|
return (_jsx("div", { className: "shepherd-root", children: _jsx(Dashboard, { workspaceId: selected?.id, config: configSection, switcher: switcher, hasWorkspace: hasWorkspace }) }));
|
|
53
54
|
}
|
package/dist/components/Chat.js
CHANGED
|
@@ -48,7 +48,7 @@ export function Chat({ announcements, selectedRepo, nowMs }) {
|
|
|
48
48
|
chatRef.current.scrollTop = chatRef.current.scrollHeight;
|
|
49
49
|
}
|
|
50
50
|
}, [messages.length]);
|
|
51
|
-
return (_jsx("div", { id: "chat", className: "chat", ref: chatRef, children: messages.length === 0 ? (_jsx("div", { className: "empty", children: "No announcements yet
|
|
51
|
+
return (_jsx("div", { id: "chat", className: "chat", ref: chatRef, children: messages.length === 0 ? (_jsx("div", { className: "empty", children: "No announcements yet. Agents will post here as they coordinate." })) : (messages.map((a, i) => {
|
|
52
52
|
const targeted = a.targetAgentName !== null || a.toAdmin;
|
|
53
53
|
const className = "msg" +
|
|
54
54
|
(targeted ? " msg--targeted" : "") +
|
|
@@ -142,5 +142,5 @@ export function Composer({ agents, selectedRepo, workspaceId, onSent }) {
|
|
|
142
142
|
accept(name);
|
|
143
143
|
}, children: [_jsx("div", { className: "ma", style: { background: colorForName(name) }, children: initialsFor(name) }), _jsx("span", { children: name })] }, name))) }), _jsxs("form", { id: "chat-form", className: "chat-form", onSubmit: (e) => {
|
|
144
144
|
void onSubmit(e);
|
|
145
|
-
}, children: [_jsx("input", { id: "chat-input", className: "chat-input", type: "text", autoComplete: "off", "aria-label": "Message the team", placeholder: "Message the team\u2026 use @name to direct it", role: "combobox", "aria-expanded": popOpen, "aria-controls": "mention-pop", "aria-autocomplete": "list", "aria-activedescendant": popOpen ? `mention-opt-${active}` : undefined, ref: inputRef, value: value, onChange: (e) => onInput(e.target.value), onKeyDown: onKeyDown }), _jsx("button", { id: "chat-send", className: "chat-send", type: "submit", disabled: sending, children: "Send" })] }), failed ? (_jsx("div", { className: "chat__note", role: "status", children: "send failed
|
|
145
|
+
}, children: [_jsx("input", { id: "chat-input", className: "chat-input", type: "text", autoComplete: "off", "aria-label": "Message the team", placeholder: "Message the team\u2026 use @name to direct it", role: "combobox", "aria-expanded": popOpen, "aria-controls": "mention-pop", "aria-autocomplete": "list", "aria-activedescendant": popOpen ? `mention-opt-${active}` : undefined, ref: inputRef, value: value, onChange: (e) => onInput(e.target.value), onKeyDown: onKeyDown }), _jsx("button", { id: "chat-send", className: "chat-send", type: "submit", disabled: sending, children: "Send" })] }), failed ? (_jsx("div", { className: "chat__note", role: "status", children: "send failed, retry" })) : null] }));
|
|
146
146
|
}
|
|
@@ -28,6 +28,11 @@ export interface DashboardProps {
|
|
|
28
28
|
* has its implicit team workspace).
|
|
29
29
|
*/
|
|
30
30
|
hasWorkspace?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Optional header logout seam. The dashboard only renders the control and
|
|
33
|
+
* invokes this callback; authentication side effects belong to the caller.
|
|
34
|
+
*/
|
|
35
|
+
onLogout?: () => void;
|
|
31
36
|
}
|
|
32
37
|
/**
|
|
33
38
|
* The Shepherd wallboard shell. Composes {@link useLandscapePolling} (which reads
|
|
@@ -51,4 +56,4 @@ export interface DashboardProps {
|
|
|
51
56
|
*
|
|
52
57
|
* @returns The dashboard element.
|
|
53
58
|
*/
|
|
54
|
-
export declare function Dashboard({ workspaceId, config, switcher, hasWorkspace, }?: DashboardProps): ReactElement;
|
|
59
|
+
export declare function Dashboard({ workspaceId, config, switcher, hasWorkspace, onLogout, }?: DashboardProps): ReactElement;
|
|
@@ -128,7 +128,7 @@ function computeCounts(snapshot) {
|
|
|
128
128
|
*
|
|
129
129
|
* @returns The dashboard element.
|
|
130
130
|
*/
|
|
131
|
-
export function Dashboard({ workspaceId, config, switcher, hasWorkspace, } = {}) {
|
|
131
|
+
export function Dashboard({ workspaceId, config, switcher, hasWorkspace, onLogout, } = {}) {
|
|
132
132
|
const hasConfig = config != null;
|
|
133
133
|
// Only the hosted shell with an explicit empty account suppresses the board;
|
|
134
134
|
// self-host (hasWorkspace undefined) always has its implicit team workspace.
|
|
@@ -275,7 +275,7 @@ export function Dashboard({ workspaceId, config, switcher, hasWorkspace, } = {})
|
|
|
275
275
|
// makes sense for a live board — hide it on the Config tab and when there is
|
|
276
276
|
// no workspace to poll. The brand and the tab strip stay in every state.
|
|
277
277
|
const showBoardChrome = !noWorkspace && activeTab !== "config";
|
|
278
|
-
return (_jsxs("div", { id: "board", children: [_jsxs("header", { children: [_jsx("h1", { className: "brand", style: { margin: 0, font: "inherit" }, children: "Shepherd" }), switcher, showBoardChrome && (_jsxs(_Fragment, { children: [_jsx(RepoSelect, { repos: repos, counts: counts, selected: effectiveRepo, onSelect: onSelectRepo }), _jsxs("span", { className: "vitals", children: [_jsx("b", { id: "vitals-online", children: online }), " online · ", _jsx("b", { id: "vitals-active", children: active }), " active"] })] })), _jsx("span", { className: "grow" }), showBoardChrome && (_jsxs(_Fragment, { children: [_jsx("span", { id: "status", className: "status" + (statusView.kind ? ` status--${statusView.kind}` : ""), children: statusView.text }), _jsx("span", { id: "freshness", className: "freshness", children: freshness })] })), _jsx("nav", { className: "tabs", role: "tablist", "aria-label": "Shepherd views", children: tabs.map(({ id, label }, index) => (_jsx("button", { ref: (el) => {
|
|
278
|
+
return (_jsxs("div", { id: "board", children: [_jsxs("header", { children: [_jsx("h1", { className: "brand", style: { margin: 0, font: "inherit" }, children: "Shepherd" }), switcher, showBoardChrome && (_jsxs(_Fragment, { children: [_jsx(RepoSelect, { repos: repos, counts: counts, selected: effectiveRepo, onSelect: onSelectRepo }), _jsxs("span", { className: "vitals", children: [_jsx("b", { id: "vitals-online", children: online }), " online · ", _jsx("b", { id: "vitals-active", children: active }), " active"] })] })), _jsx("span", { className: "grow" }), showBoardChrome && (_jsxs(_Fragment, { children: [_jsx("span", { id: "status", className: "status" + (statusView.kind ? ` status--${statusView.kind}` : ""), children: statusView.text }), _jsx("span", { id: "freshness", className: "freshness", children: freshness })] })), onLogout && (_jsx("button", { type: "button", className: "header-signout", onClick: onLogout, children: "Sign out" })), _jsx("nav", { className: "tabs", role: "tablist", "aria-label": "Shepherd views", children: tabs.map(({ id, label }, index) => (_jsx("button", { ref: (el) => {
|
|
279
279
|
tabRefs.current[index] = el;
|
|
280
280
|
}, className: "tab" + (activeTab === id ? " tab--active" : ""), "data-tab": id, type: "button", role: "tab", id: `tab-${id}`, "aria-controls": `panel-${id}`, "aria-selected": activeTab === id, tabIndex: activeTab === id ? 0 : -1, onClick: () => onTab(id), onKeyDown: (e) => onTabKeyDown(e, index), children: label }, id))) })] }), _jsx("section", { id: "panel-tasks", role: "tabpanel", "aria-labelledby": "tab-tasks", hidden: activeTab !== "tasks", children: noWorkspace ? (_jsx(EmptyState, { onGetStarted: () => onTab("config") })) : (_jsxs(_Fragment, { children: [_jsx(Crew, { agents: agents, tasks: tasks, selectedRepo: effectiveRepo }), _jsxs("div", { className: "board", children: [_jsxs("div", { className: "col", children: [_jsxs("div", { className: "colhead", children: [_jsx("h2", { children: "Active" }), _jsx("span", { className: "n", id: "active-count", children: activeCount })] }), _jsx(ActiveList, { tasks: tasks, nowMs: nowMs, selectedRepo: effectiveRepo })] }), _jsx("div", { className: "board__rule" }), _jsxs("div", { className: "col", children: [_jsxs("div", { className: "colhead", children: [_jsx("h2", { children: "Done" }), _jsx("span", { className: "n", id: "done-count", children: doneCount })] }), _jsx(DoneList, { tasks: tasks, nowMs: nowMs, selectedRepo: effectiveRepo, doneShown: doneShown, onLoadMore: onLoadMore })] })] })] })) }), _jsx("section", { id: "panel-chat", role: "tabpanel", "aria-labelledby": "tab-chat", hidden: activeTab !== "chat", children: noWorkspace ? (_jsx(EmptyState, { onGetStarted: () => onTab("config") })) : (_jsxs("div", { className: "chat-wrap", children: [_jsx(Chat, { announcements: announcements, selectedRepo: effectiveRepo, nowMs: nowMs }), _jsx(Composer, { agents: agents, selectedRepo: effectiveRepo, workspaceId: workspaceId, onSent: refresh })] })) }), hasConfig && (_jsx("section", { id: "panel-config", role: "tabpanel", "aria-labelledby": "tab-config", hidden: activeTab !== "config", children: config })), _jsx(FeedbackWidget, { workspaceId: workspaceId })] }));
|
|
281
281
|
}
|
|
@@ -36,7 +36,7 @@ export function DoneList({ tasks, nowMs, selectedRepo, doneShown, onLoadMore, })
|
|
|
36
36
|
lastDay = day;
|
|
37
37
|
const dropped = t.status === "dropped";
|
|
38
38
|
const meta = dropped
|
|
39
|
-
? `went offline ${formatRelative(t.endedAt ?? t.createdAt, nowMs)}
|
|
39
|
+
? `went offline ${formatRelative(t.endedAt ?? t.createdAt, nowMs)}, no done signal`
|
|
40
40
|
: `finished ${formatRelative(t.endedAt ?? t.createdAt, nowMs)}${formatActiveDuration(t.createdAt, t.endedAt)
|
|
41
41
|
? " · " + formatActiveDuration(t.createdAt, t.endedAt)
|
|
42
42
|
: ""}`;
|
|
@@ -12,5 +12,10 @@ export interface ConfigPanelProps {
|
|
|
12
12
|
onLeft?: () => void;
|
|
13
13
|
/** Called after a successful delete, so the shell re-lists its workspaces. */
|
|
14
14
|
onDeleted?: () => void;
|
|
15
|
+
/**
|
|
16
|
+
* Hosted session logout hook. Shepherd renders the control while the host owns
|
|
17
|
+
* the authentication side effect.
|
|
18
|
+
*/
|
|
19
|
+
onLogout?: () => void;
|
|
15
20
|
}
|
|
16
|
-
export declare function ConfigPanel({ workspace, hubUrl, membersRefreshKey, onMembersChanged, onLeft, onDeleted, }: ConfigPanelProps): import("react").JSX.Element;
|
|
21
|
+
export declare function ConfigPanel({ workspace, hubUrl, membersRefreshKey, onMembersChanged, onLeft, onDeleted, onLogout, }: ConfigPanelProps): import("react").JSX.Element;
|
|
@@ -4,13 +4,14 @@ import { GeneralSettings } from "./GeneralSettings.js";
|
|
|
4
4
|
import { Members } from "./Members.js";
|
|
5
5
|
import { Invites } from "./Invites.js";
|
|
6
6
|
import { ConnectAgent } from "./ConnectAgent.js";
|
|
7
|
+
import { SignOutAction } from "./SignOutAction.js";
|
|
7
8
|
const SECTIONS = [
|
|
8
9
|
{ id: "general", label: "General" },
|
|
9
10
|
{ id: "members", label: "Members" },
|
|
10
11
|
{ id: "agent", label: "Agent" },
|
|
11
12
|
];
|
|
12
|
-
export function ConfigPanel({ workspace, hubUrl, membersRefreshKey, onMembersChanged, onLeft, onDeleted, }) {
|
|
13
|
+
export function ConfigPanel({ workspace, hubUrl, membersRefreshKey, onMembersChanged, onLeft, onDeleted, onLogout, }) {
|
|
13
14
|
const [section, setSection] = useState("general");
|
|
14
15
|
const isAdmin = workspace.role === "admin";
|
|
15
|
-
return (_jsxs("div", { className: "config-layout", children: [_jsx("nav", { className: "config-nav", "aria-label": "Configuration sections", children: SECTIONS.map(({ id, label }) => (_jsx("button", { type: "button", className: "config-nav__item" + (section === id ? " config-nav__item--on" : ""), "aria-current": section === id ? "page" : undefined, onClick: () => setSection(id), children: label }, id))) }), _jsxs("div", { className: "config-panel", children: [section === "general" && (_jsx(GeneralSettings, { workspace: workspace, onLeft: onLeft, onDeleted: onDeleted })), section === "members" && (_jsxs(_Fragment, { children: [_jsx(Members, { workspaceId: workspace.id, refreshKey: membersRefreshKey, canRemove: isAdmin }), isAdmin && (_jsx(Invites, { workspaceId: workspace.id, onMembersChanged: onMembersChanged }))] })), section === "agent" && _jsx(ConnectAgent, { hubUrl: hubUrl })] })] }));
|
|
16
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "config-layout", children: [_jsx("nav", { className: "config-nav", "aria-label": "Configuration sections", children: SECTIONS.map(({ id, label }) => (_jsx("button", { type: "button", className: "config-nav__item" + (section === id ? " config-nav__item--on" : ""), "aria-current": section === id ? "page" : undefined, onClick: () => setSection(id), children: label }, id))) }), _jsxs("div", { className: "config-panel", children: [section === "general" && (_jsx(GeneralSettings, { workspace: workspace, onLeft: onLeft, onDeleted: onDeleted })), section === "members" && (_jsxs(_Fragment, { children: [_jsx(Members, { workspaceId: workspace.id, refreshKey: membersRefreshKey, canRemove: isAdmin }), isAdmin && (_jsx(Invites, { workspaceId: workspace.id, onMembersChanged: onMembersChanged }))] })), section === "agent" && _jsx(ConnectAgent, { hubUrl: hubUrl })] })] }), onLogout && _jsx(SignOutAction, { onLogout: onLogout })] }));
|
|
16
17
|
}
|
|
@@ -24,5 +24,5 @@ export function ConfirmDeleteWorkspace({ workspaceName, busy, error, onConfirm,
|
|
|
24
24
|
onCancel();
|
|
25
25
|
}, children: _jsxs("div", { className: "shepherd-modal", role: "dialog", "aria-modal": "true", "aria-labelledby": headingId, "aria-describedby": descId,
|
|
26
26
|
// Clicks inside the dialog must not bubble to the backdrop's cancel.
|
|
27
|
-
onClick: (e) => e.stopPropagation(), children: [_jsx("h3", { id: headingId, children: "Delete workspace" }), _jsxs("p", { id: descId, className: "shepherd-modal__body", children: ["This permanently deletes ", _jsx("b", { children: workspaceName }), " and all of its data
|
|
27
|
+
onClick: (e) => e.stopPropagation(), children: [_jsx("h3", { id: headingId, children: "Delete workspace" }), _jsxs("p", { id: descId, className: "shepherd-modal__body", children: ["This permanently deletes ", _jsx("b", { children: workspaceName }), " and all of its data: agents, sessions, tasks, announcements, change history, tokens, invites, and members. This cannot be undone."] }), _jsxs("label", { htmlFor: `${headingId}-input`, className: "shepherd-modal__label", children: ["Type ", _jsx("b", { children: workspaceName }), " to confirm"] }), _jsx("input", { id: `${headingId}-input`, ref: inputRef, type: "text", value: typed, autoComplete: "off", disabled: busy, onChange: (e) => setTyped(e.target.value) }), error && _jsx("p", { role: "alert", children: error }), _jsxs("div", { className: "shepherd-modal__actions", children: [_jsx("button", { type: "button", onClick: onCancel, disabled: busy, children: "Cancel" }), _jsx("button", { type: "button", className: "danger", onClick: onConfirm, disabled: !confirmed || busy, children: busy ? "Deleting…" : "Delete workspace" })] })] }) }));
|
|
28
28
|
}
|
|
@@ -206,12 +206,12 @@ export function ConnectAgent({ hubUrl }) {
|
|
|
206
206
|
setCopied(true);
|
|
207
207
|
setTimeout(() => setCopied(false), 2000);
|
|
208
208
|
}
|
|
209
|
-
return (_jsxs("section", { className: "shepherd-connect-agent", "aria-labelledby": headingId, children: [_jsxs("div", { className: "card-head", children: [_jsx("h3", { id: headingId, children: "Connect your agent" }), _jsx("p", { className: "card-sub", children: "Generate a token
|
|
209
|
+
return (_jsxs("section", { className: "shepherd-connect-agent", "aria-labelledby": headingId, children: [_jsxs("div", { className: "card-head", children: [_jsx("h3", { id: headingId, children: "Connect your agent" }), _jsx("p", { className: "card-sub", children: "Generate a token; it works across all your workspaces, so paste the command into your coding tool." }), _jsx("p", { className: "card-sub", children: "The first time the agent changes files in a repo, Shepherd asks which workspace to coordinate that repo with, once per repo, right in your tool." })] }), _jsxs("div", { className: "card-body", children: [_jsxs("div", { className: "field", children: [_jsx("label", { htmlFor: "connect-tool", children: "Tool" }), _jsx("select", { id: "connect-tool", value: tool, onChange: (e) => setTool(e.target.value), children: TOOLS.map((t) => (_jsx("option", { value: t.id, children: t.label }, t.id))) })] }), _jsxs("div", { className: "field generate", children: [_jsx("label", { htmlFor: "token-name", children: "Token name (optional)" }), _jsxs("div", { className: "field__row", children: [_jsx("input", { id: "token-name", type: "text", value: name, onChange: (e) => setName(e.target.value), onKeyDown: (e) => {
|
|
210
210
|
if (e.key === "Enter" && !busy) {
|
|
211
211
|
e.preventDefault();
|
|
212
212
|
void generate();
|
|
213
213
|
}
|
|
214
|
-
}, placeholder: "e.g. laptop" }), _jsx("button", { type: "button", onClick: () => void generate(), disabled: busy, children: "Generate token" })] })] }), error && _jsx("p", { role: "alert", children: error }), status && _jsx("p", { role: "status", children: status }), rawToken && (_jsx("p", { className: "token-once", role: "status", children: "Copy this token now
|
|
214
|
+
}, placeholder: "e.g. laptop" }), _jsx("button", { type: "button", onClick: () => void generate(), disabled: busy, children: "Generate token" })] })] }), error && _jsx("p", { role: "alert", children: error }), status && _jsx("p", { role: "status", children: status }), rawToken && (_jsx("p", { className: "token-once", role: "status", children: "Copy this token now; it won't be shown again." })), _jsxs("div", { className: "install-command", children: [_jsx("pre", { "data-testid": "install-command", children: command }), _jsx("button", { type: "button", className: "install-command__copy", "aria-label": copied ? "Copied" : "Copy command", title: copied ? "Copied" : "Copy", onClick: () => void copyCommand(), children: copied ? (_jsx("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M20 6 9 17l-5-5" }) })) : (_jsxs("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }), _jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })] })) })] }), hook && (_jsxs("div", { className: "hook-setup", children: [_jsxs("p", { className: "card-sub", children: ["Message delivery sets itself up: the first time the agent runs, Shepherd adds its inbox hook to ", _jsx("code", { children: hook.target }), " ", "automatically. It delivers teammate announcements and reminds the agent to link each repo before its first write. This happens at most once; set", " ", _jsx("code", { children: "SHEPHERD_NO_AUTO_HOOKS=1" }), " to opt out."] }), hook.snippet && (_jsxs("details", { className: "hook-reference", children: [_jsx("summary", { children: "What gets added (manual equivalent)" }), _jsx("div", { className: "install-command", children: _jsx("pre", { "data-testid": "hook-snippet", children: hook.snippet }) })] }))] })), _jsx("h4", { children: "Existing tokens" }), loading ? (_jsx("p", { role: "status", children: "Loading\u2026" })) : tokens.length === 0 ? (_jsx("p", { children: "No tokens yet." })) : (_jsx("ul", { children: tokens.map((t) => {
|
|
215
215
|
const name = t.name ?? t.id;
|
|
216
216
|
return (_jsxs("li", { children: [_jsx("span", { children: name }), _jsx("span", { className: "token-meta", children: tokenMeta(t, Date.now()) }), t.revokedAt ? (_jsx("span", { className: "revoked", children: "revoked" })) : (_jsx("button", { type: "button", "aria-label": `Revoke token ${name}`, onClick: () => void revoke(t.id), disabled: revokingId === t.id, children: "Revoke" }))] }, t.id));
|
|
217
217
|
}) }))] })] }));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Props for the hosted session sign out presentation control. */
|
|
2
|
+
export interface SignOutActionProps {
|
|
3
|
+
/** Called immediately when the operator chooses to end this dashboard session. */
|
|
4
|
+
onLogout: () => void;
|
|
5
|
+
}
|
|
6
|
+
/** Renders the session-level sign out control supplied by the host shell. */
|
|
7
|
+
export declare function SignOutAction({ onLogout }: SignOutActionProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId } from "react";
|
|
3
|
+
/** Renders the session-level sign out control supplied by the host shell. */
|
|
4
|
+
export function SignOutAction({ onLogout }) {
|
|
5
|
+
const headingId = useId();
|
|
6
|
+
return (_jsxs("section", { className: "config-signout", "aria-labelledby": headingId, children: [_jsx("div", { className: "card-head", children: _jsx("h3", { id: headingId, children: "Session" }) }), _jsxs("div", { className: "card-body", children: [_jsx("p", { className: "helper", children: "Sign out of this Shepherd dashboard session." }), _jsx("button", { type: "button", onClick: onLogout, children: "Sign out" })] })] }));
|
|
7
|
+
}
|