sitevision-cli 1.0.0-beta.13 → 1.0.0-beta.14
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/dist/cli.js +70 -41
- package/dist/commands/build.js +1 -1
- package/dist/commands/dev.d.ts +8 -10
- package/dist/commands/dev.js +75 -390
- package/dist/commands/watch.js +5 -23
- package/dist/components/AuthLoginScreen.js +2 -1
- package/dist/components/DevPropertiesForm.js +6 -3
- package/dist/components/PasswordInput.js +2 -1
- package/dist/shell/AddonPicker.d.ts +14 -0
- package/dist/shell/AddonPicker.js +54 -0
- package/dist/shell/CommandPalette.d.ts +8 -0
- package/dist/shell/CommandPalette.js +63 -0
- package/dist/shell/ConfigForm.d.ts +35 -0
- package/dist/shell/ConfigForm.js +472 -0
- package/dist/shell/Frame.d.ts +52 -0
- package/dist/shell/Frame.js +98 -0
- package/dist/shell/Settings.d.ts +6 -0
- package/dist/shell/Settings.js +96 -0
- package/dist/shell/Shell.d.ts +8 -0
- package/dist/shell/Shell.js +520 -0
- package/dist/shell/Tabs.d.ts +36 -0
- package/dist/shell/Tabs.js +85 -0
- package/dist/shell/actions.d.ts +45 -0
- package/dist/shell/actions.js +0 -0
- package/dist/types/index.d.ts +12 -2
- package/dist/utils/config.d.ts +10 -0
- package/dist/utils/config.js +14 -0
- package/dist/utils/environments.d.ts +20 -0
- package/dist/utils/environments.js +74 -0
- package/dist/utils/i18n.d.ts +12 -0
- package/dist/utils/i18n.js +263 -0
- package/dist/utils/oauth2-auth.d.ts +1 -0
- package/dist/utils/oauth2-auth.js +4 -3
- package/dist/utils/project-detection.d.ts +23 -1
- package/dist/utils/project-detection.js +124 -51
- package/dist/utils/sitevision-api.d.ts +35 -0
- package/dist/utils/sitevision-api.js +74 -1
- package/dist/utils/tasks.d.ts +48 -0
- package/dist/utils/tasks.js +371 -0
- package/dist/utils/workspace.d.ts +8 -0
- package/dist/utils/workspace.js +48 -0
- package/package.json +1 -1
- package/readme.md +76 -24
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { ProjectInfo } from '../types/index.js';
|
|
3
|
+
import { type Task } from '../utils/tasks.js';
|
|
4
|
+
export declare const ACCENT = "cyan";
|
|
5
|
+
export declare const NARROW_BELOW = 100;
|
|
6
|
+
/** Navigator width for a terminal: a quarter of the columns, within 32..48. */
|
|
7
|
+
export declare function navWidth(columns: number): number;
|
|
8
|
+
export interface TopBarProps {
|
|
9
|
+
context: string;
|
|
10
|
+
domain?: string;
|
|
11
|
+
auth: {
|
|
12
|
+
ready: boolean;
|
|
13
|
+
label: string;
|
|
14
|
+
};
|
|
15
|
+
version: string;
|
|
16
|
+
environment?: {
|
|
17
|
+
name: string;
|
|
18
|
+
color: 'green' | 'yellow' | 'red';
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export declare function TopBar({ context, domain, auth, version, environment, }: TopBarProps): import("react").JSX.Element;
|
|
22
|
+
/** Three-letter type marker; unknown manifest types show as "???". */
|
|
23
|
+
export declare function typeGlyph(manifest: ProjectInfo['manifest']): string;
|
|
24
|
+
export declare function appStatus(project: ProjectInfo): {
|
|
25
|
+
deps: boolean;
|
|
26
|
+
config: boolean;
|
|
27
|
+
sync: number;
|
|
28
|
+
signing: boolean;
|
|
29
|
+
scriptsWarning: string | undefined;
|
|
30
|
+
};
|
|
31
|
+
export interface NavigatorProps {
|
|
32
|
+
apps: ProjectInfo[];
|
|
33
|
+
groupOf: (app: ProjectInfo) => string;
|
|
34
|
+
selected: number;
|
|
35
|
+
focused: boolean;
|
|
36
|
+
tasks: Task[];
|
|
37
|
+
height: number;
|
|
38
|
+
single: boolean;
|
|
39
|
+
settingsSelected?: boolean;
|
|
40
|
+
width: number;
|
|
41
|
+
}
|
|
42
|
+
export declare function Navigator({ apps, groupOf, selected, focused, tasks, height, single, settingsSelected, width, }: NavigatorProps): import("react").JSX.Element;
|
|
43
|
+
export declare function NavigatorStrip({ apps, selected, focused, }: Pick<NavigatorProps, 'apps' | 'selected' | 'focused'>): import("react").JSX.Element;
|
|
44
|
+
export declare function elapsed(task: Task): string;
|
|
45
|
+
export interface Hint {
|
|
46
|
+
key: string;
|
|
47
|
+
label: string;
|
|
48
|
+
}
|
|
49
|
+
export declare function BottomBar({ hints, right }: {
|
|
50
|
+
hints: Hint[];
|
|
51
|
+
right: ReactNode;
|
|
52
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import Spinner from 'ink-spinner';
|
|
4
|
+
import { appTypeOf, getPackageJsonSyncChanges, localizedText, } from '../utils/project-detection.js';
|
|
5
|
+
import { t } from '../utils/i18n.js';
|
|
6
|
+
export const ACCENT = 'cyan';
|
|
7
|
+
export const NARROW_BELOW = 100;
|
|
8
|
+
/** Navigator width for a terminal: a quarter of the columns, within 32..48. */
|
|
9
|
+
export function navWidth(columns) {
|
|
10
|
+
return Math.min(48, Math.max(32, Math.floor(columns / 4)));
|
|
11
|
+
}
|
|
12
|
+
export function TopBar({ context, domain, auth, version, environment, }) {
|
|
13
|
+
return (_jsxs(Box, { paddingX: 1, justifyContent: "space-between", height: 1, children: [_jsx(Box, { flexShrink: 1, marginRight: 2, children: _jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { bold: true, color: ACCENT, children: "svc" }), environment && (_jsxs(Text, { children: [' ', _jsx(Text, { backgroundColor: environment.color, color: "black", bold: true, children: ` ${environment.name.toUpperCase()} ` })] })), _jsxs(Text, { dimColor: true, children: [" ", context] })] }) }), _jsx(Box, { flexShrink: 0, children: _jsxs(Text, { wrap: "truncate", children: [domain && _jsxs(Text, { dimColor: true, children: [domain, " "] }), _jsx(Text, { color: auth.ready ? 'green' : 'yellow', children: auth.ready ? '●' : '○' }), _jsxs(Text, { children: [" ", auth.label, " "] }), _jsxs(Text, { dimColor: true, children: ["v", version] })] }) })] }));
|
|
14
|
+
}
|
|
15
|
+
const TYPE_GLYPH = {
|
|
16
|
+
web: 'web',
|
|
17
|
+
widget: 'wgt',
|
|
18
|
+
rest: 'rst',
|
|
19
|
+
mcp: 'mcp',
|
|
20
|
+
};
|
|
21
|
+
/** Three-letter type marker; unknown manifest types show as "???". */
|
|
22
|
+
export function typeGlyph(manifest) {
|
|
23
|
+
const type = appTypeOf(manifest);
|
|
24
|
+
return type ? TYPE_GLYPH[type] : '???';
|
|
25
|
+
}
|
|
26
|
+
export function appStatus(project) {
|
|
27
|
+
const sync = project.devProperties
|
|
28
|
+
? getPackageJsonSyncChanges(project.root, project.devProperties).length
|
|
29
|
+
: 0;
|
|
30
|
+
return {
|
|
31
|
+
deps: project.hasNodeModules,
|
|
32
|
+
config: Boolean(project.devProperties),
|
|
33
|
+
sync,
|
|
34
|
+
signing: project.hasSigningProperties,
|
|
35
|
+
scriptsWarning: undefined,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function Dots({ project }) {
|
|
39
|
+
const s = appStatus(project);
|
|
40
|
+
const dot = (ok, warn = false) => (_jsx(Text, { color: ok ? 'green' : warn ? 'yellow' : 'red', children: "\u25CF" }));
|
|
41
|
+
return (_jsxs(Text, { children: [dot(s.deps), dot(s.config), dot(s.sync === 0, s.sync > 0), dot(s.signing, !s.signing)] }));
|
|
42
|
+
}
|
|
43
|
+
export function Navigator({ apps, groupOf, selected, focused, tasks, height, single, settingsSelected = false, width, }) {
|
|
44
|
+
// Row: marker(1) glyph(3) sp name sp version(6) sp dots(4) inside the padding.
|
|
45
|
+
const nameWidth = width - 2 - 17;
|
|
46
|
+
const running = tasks.filter(task => task.status === 'running');
|
|
47
|
+
// Every row is exactly one line; nothing may shrink or the rows overlap.
|
|
48
|
+
const rows = [];
|
|
49
|
+
const rowApp = [];
|
|
50
|
+
let lastGroup;
|
|
51
|
+
for (const [index, app] of apps.entries()) {
|
|
52
|
+
const group = groupOf(app);
|
|
53
|
+
if (!single && group !== lastGroup) {
|
|
54
|
+
rows.push(_jsx(Box, { height: 1, flexShrink: 0, children: _jsxs(Text, { dimColor: true, wrap: "truncate", children: [' ', group] }) }, `g-${group}`));
|
|
55
|
+
rowApp.push(-1);
|
|
56
|
+
lastGroup = group;
|
|
57
|
+
}
|
|
58
|
+
rowApp.push(index);
|
|
59
|
+
const active = index === selected;
|
|
60
|
+
const busy = running.some(task => task.appRoot === app.root);
|
|
61
|
+
const name = localizedText(app.manifest.name) || app.manifest.id;
|
|
62
|
+
rows.push(_jsxs(Box, { width: width - 2, height: 1, flexShrink: 0, children: [_jsxs(Text, { backgroundColor: active && focused ? ACCENT : undefined, color: active && focused ? 'black' : undefined, bold: active, wrap: "truncate", children: [active ? '▎' : ' ', _jsx(Text, { dimColor: !active, children: typeGlyph(app.manifest) }), ' ', name.padEnd(nameWidth).slice(0, nameWidth), ' ', _jsx(Text, { dimColor: true, children: app.manifest.version.padStart(6).slice(0, 6) }), ' '] }), busy ? (_jsx(Text, { color: ACCENT, children: _jsx(Spinner, { type: "dots" }) })) : (_jsx(Dots, { project: app }))] }, app.root));
|
|
63
|
+
}
|
|
64
|
+
// Window the list so the selected app stays visible; the lines outside
|
|
65
|
+
// are summarised as "… n more".
|
|
66
|
+
const fixed = 1 + 1 + (single ? 0 : 2) + (running.length > 0 ? running.length + 2 : 0);
|
|
67
|
+
const avail = Math.max(3, height - fixed);
|
|
68
|
+
let shown = rows;
|
|
69
|
+
if (rows.length > avail) {
|
|
70
|
+
const target = settingsSelected
|
|
71
|
+
? rows.length - 1
|
|
72
|
+
: rowApp.indexOf(selected);
|
|
73
|
+
const start = Math.max(0, Math.min(target - Math.floor(avail / 2), rows.length - avail));
|
|
74
|
+
const end = start + avail;
|
|
75
|
+
shown = rows.slice(start, end);
|
|
76
|
+
const more = (n, arrow) => (_jsx(Box, { height: 1, flexShrink: 0, children: _jsxs(Text, { dimColor: true, children: [' ', t('{arrow} {n} more', { arrow, n })] }) }, `more-${arrow}`));
|
|
77
|
+
if (start > 0)
|
|
78
|
+
shown[0] = more(start, '↑');
|
|
79
|
+
if (end < rows.length)
|
|
80
|
+
shown[shown.length - 1] = more(rows.length - end, '↓');
|
|
81
|
+
}
|
|
82
|
+
return (_jsxs(Box, { flexDirection: "column", width: width, height: height, borderStyle: "single", borderDimColor: true, borderTop: false, borderBottom: false, borderLeft: false, paddingX: 1, overflow: "hidden", children: [_jsx(Text, { bold: true, dimColor: true, children: single
|
|
83
|
+
? t('APP')
|
|
84
|
+
: apps.length === 1
|
|
85
|
+
? t('WORKSPACE 1 app')
|
|
86
|
+
: t('WORKSPACE {n} apps', { n: apps.length }) }), shown, _jsx(Text, { dimColor: true, children: ' ' + t('deps·config·sync·signing') }), !single && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { backgroundColor: settingsSelected && focused ? ACCENT : undefined, color: settingsSelected && focused ? 'black' : undefined, bold: settingsSelected, children: [settingsSelected ? '▎' : ' ', "\u2699 ", t('Workspace settings')] }) })), running.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, dimColor: true, children: t('TASKS') }), running.map(task => (_jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { color: ACCENT, children: _jsx(Spinner, { type: "dots" }) }), ' ', task.label, " ", task.appName, " ", _jsx(Text, { dimColor: true, children: elapsed(task) })] }, task.id)))] }))] }));
|
|
87
|
+
}
|
|
88
|
+
export function NavigatorStrip({ apps, selected, focused, }) {
|
|
89
|
+
return (_jsx(Box, { paddingX: 1, children: _jsx(Text, { wrap: "truncate", children: apps.map((app, index) => (_jsxs(Text, { backgroundColor: index === selected && focused ? ACCENT : undefined, color: index === selected && focused ? 'black' : undefined, bold: index === selected, children: [' ', typeGlyph(app.manifest), ' ', localizedText(app.manifest.name) || app.manifest.id, ' '] }, app.root))) }) }));
|
|
90
|
+
}
|
|
91
|
+
export function elapsed(task) {
|
|
92
|
+
const ms = (task.endedAt ?? Date.now()) - task.startedAt;
|
|
93
|
+
const s = Math.round(ms / 1000);
|
|
94
|
+
return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m ${s % 60}s`;
|
|
95
|
+
}
|
|
96
|
+
export function BottomBar({ hints, right }) {
|
|
97
|
+
return (_jsxs(Box, { paddingX: 1, justifyContent: "space-between", children: [_jsx(Text, { wrap: "truncate", children: hints.map(h => (_jsxs(Text, { children: [_jsx(Text, { bold: true, color: ACCENT, children: h.key }), _jsxs(Text, { dimColor: true, children: [" ", h.label, " "] })] }, h.key + h.label))) }), right] }));
|
|
98
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
import { getSettings, setSettings, settingsFile } from '../utils/config.js';
|
|
5
|
+
import { LANGUAGES, LANGUAGE_NAMES, setLanguage, t, } from '../utils/i18n.js';
|
|
6
|
+
import { ACCENT } from './Frame.js';
|
|
7
|
+
const ROWS = [
|
|
8
|
+
{
|
|
9
|
+
key: 'language',
|
|
10
|
+
label: 'Language',
|
|
11
|
+
options: LANGUAGES,
|
|
12
|
+
display: value => LANGUAGE_NAMES[value],
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
key: 'introAnimation',
|
|
16
|
+
label: 'Intro animation',
|
|
17
|
+
options: ['on', 'off'],
|
|
18
|
+
display: value => t(value),
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
/** Global preferences, saved per field like the Config tab. */
|
|
22
|
+
export function SettingsScreen({ onChanged, onClose, onOpenWorkspace, }) {
|
|
23
|
+
const [values, setValues] = useState(() => {
|
|
24
|
+
const s = getSettings();
|
|
25
|
+
return {
|
|
26
|
+
language: s.language,
|
|
27
|
+
introAnimation: s.introAnimation ? 'on' : 'off',
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
const [cursor, setCursor] = useState(0);
|
|
31
|
+
const [editing, setEditing] = useState(false);
|
|
32
|
+
const [draft, setDraft] = useState('');
|
|
33
|
+
const [note, setNote] = useState('');
|
|
34
|
+
const rowCount = ROWS.length + (onOpenWorkspace ? 1 : 0);
|
|
35
|
+
const onWorkspaceRow = cursor === ROWS.length;
|
|
36
|
+
const current = ROWS[Math.min(cursor, ROWS.length - 1)];
|
|
37
|
+
const commit = (value) => {
|
|
38
|
+
const next = { ...values, [current.key]: value };
|
|
39
|
+
setValues(next);
|
|
40
|
+
if (current.key === 'language')
|
|
41
|
+
setLanguage(value);
|
|
42
|
+
setSettings({
|
|
43
|
+
language: next.language,
|
|
44
|
+
introAnimation: next.introAnimation === 'on',
|
|
45
|
+
});
|
|
46
|
+
setNote(t('Saved.'));
|
|
47
|
+
onChanged();
|
|
48
|
+
};
|
|
49
|
+
useInput((input, key) => {
|
|
50
|
+
if (editing) {
|
|
51
|
+
if (key.escape) {
|
|
52
|
+
setEditing(false);
|
|
53
|
+
}
|
|
54
|
+
else if (key.return) {
|
|
55
|
+
setEditing(false);
|
|
56
|
+
if (draft !== values[current.key])
|
|
57
|
+
commit(draft);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const step = key.leftArrow || key.upArrow
|
|
61
|
+
? -1
|
|
62
|
+
: key.rightArrow || key.downArrow || input === ' '
|
|
63
|
+
? 1
|
|
64
|
+
: 0;
|
|
65
|
+
if (step !== 0) {
|
|
66
|
+
const i = current.options.indexOf(draft);
|
|
67
|
+
const n = current.options.length;
|
|
68
|
+
setDraft(current.options[(i + step + n) % n]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (key.escape)
|
|
74
|
+
onClose();
|
|
75
|
+
else if (key.downArrow || key.tab)
|
|
76
|
+
setCursor(c => (c + 1) % rowCount);
|
|
77
|
+
else if (key.upArrow)
|
|
78
|
+
setCursor(c => (c - 1 + rowCount) % rowCount);
|
|
79
|
+
else if (key.return) {
|
|
80
|
+
if (onWorkspaceRow) {
|
|
81
|
+
onOpenWorkspace?.();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
setDraft(values[current.key]);
|
|
85
|
+
setEditing(true);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: ACCENT, paddingX: 1, width: 64, children: [_jsxs(Text, { children: [_jsx(Text, { bold: true, children: t('Settings') }), _jsxs(Text, { dimColor: true, children: [" \u00B7 ", t('stored in {file}', { file: settingsFile() })] })] }), ROWS.map((row, i) => {
|
|
89
|
+
const focused = i === cursor && !onWorkspaceRow;
|
|
90
|
+
const typing = focused && editing;
|
|
91
|
+
const chosen = typing ? draft : values[row.key];
|
|
92
|
+
return (_jsxs(Box, { height: 1, children: [_jsx(Text, { color: focused ? ACCENT : undefined, bold: focused, dimColor: !focused, children: (focused ? '▸ ' : ' ') + t(row.label).padEnd(18) }), _jsx(Text, { children: row.options.map((option, j) => (_jsxs(Text, { children: [_jsx(Text, { bold: option === chosen, color: option === chosen ? ACCENT : undefined, inverse: typing && option === chosen, dimColor: option !== chosen, children: row.display(option) }), j < row.options.length - 1 && _jsx(Text, { dimColor: true, children: " \u00B7 " })] }, option))) })] }, row.key));
|
|
93
|
+
}), onOpenWorkspace && (_jsxs(Box, { height: 1, children: [_jsx(Text, { color: onWorkspaceRow ? ACCENT : undefined, bold: onWorkspaceRow, dimColor: !onWorkspaceRow, children: (onWorkspaceRow ? '▸ ' : ' ') + t('Workspace config').padEnd(18) }), _jsx(Text, { dimColor: true, children: t('shared auth and site config for every app') })] })), _jsx(Text, { color: "yellow", children: note }), _jsx(Text, { dimColor: true, children: editing
|
|
94
|
+
? t('←→ choose · Enter confirm · Esc cancel')
|
|
95
|
+
: t('↑↓ setting · Enter edit · Esc close') })] }));
|
|
96
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ProjectInfo } from '../types/index.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
apps: ProjectInfo[];
|
|
4
|
+
workspaceRoot?: string;
|
|
5
|
+
version: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function Shell({ apps: initialApps, workspaceRoot, version }: Props): import("react").JSX.Element;
|
|
8
|
+
export {};
|