feat: port the demo redesign with Manage and Settings tabs

This commit is contained in:
LukeGus committed 2026-10-05 16:23:21 -05:00
1 parent f8151cb885
commit 35c954b79a
128 files changed
+13262 -8143

No files matched your search

+46 -1
View File
@@ -34,6 +34,7 @@ const ALLOWED = [
"AlertDialogTitle",
"AlertDialogTrigger",
"AlertTitle",
"BackButton",
"Badge",
"BarSeries",
"BarSeriesItem",
@@ -48,17 +49,25 @@ const ALLOWED = [
"CardFooter",
"CardGridCanvas",
"CardHeader",
"CardMasonry",
"CardTitle",
"Checkbox",
"ColumnCountStepper",
"ComponentSlot",
"ConfirmOptions",
"ConnectionGate",
"ConnectionGateController",
"ConnectionGateProps",
"ConnectionLogPanel",
"ConnectionLogProvider",
"ConnectionOrigin",
"ConnectionScreen",
"ConnectionStage",
"ConnectionStatus",
"ConnectionUnavailable",
"CustomKeybinding",
"DataColumn",
"DataView",
"Dialog",
"DialogClose",
"DialogContent",
@@ -84,20 +93,27 @@ const ALLOWED = [
"DropdownMenuSubContent",
"DropdownMenuSubTrigger",
"DropdownMenuTrigger",
"EditingWidth",
"EmptyState",
"FOLDER_COLORS",
"Facts",
"FakeSwitch",
"Field",
"FieldPair",
"FullScreenAppPhase",
"FullScreenAppWrapper",
"GridCardCatalogEntry",
"GridColSpan",
"GridColumns",
"GridLayout",
"GridSlot",
"GroupHeading",
"HostDefaultBadge",
"HostDefaultField",
"HostFeatureFields",
"HostFeatureFieldsProps",
"HostKeyVerificationDialog",
"InlineView",
"Input",
"Kbd",
"KbdKey",
@@ -114,8 +130,19 @@ const ALLOWED = [
"ManagerCardError",
"ManagerCardShell",
"ManagerSearch",
"Meter",
"MetricCard",
"MiniStat",
"NumberField",
"PANEL",
"PanePrompt",
"PROMPT_BUTTON",
"PROMPT_DESTRUCTIVE_BUTTON",
"PROMPT_PRIMARY_BUTTON",
"PanelDensity",
"PanelSearch",
"PanelShell",
"PanelViewMode",
"PassphraseDialog",
"PasswordInput",
"PluginComponent",
@@ -127,14 +154,18 @@ const ALLOWED = [
"RadialGauge",
"RadialGaugeProps",
"RecentActivityItem",
"Repeater",
"RobustClipboardProvider",
"SSHAuthDialog",
"ScrollArea",
"ScrollBar",
"SecretField",
"SectionCard",
"Segmented",
"Select",
"Select2",
"SelectContent",
"SelectField",
"SelectGroup",
"SelectItem",
"SelectLabel",
@@ -158,8 +189,16 @@ const ALLOWED = [
"Sparkline",
"SparklineProps",
"StatRow",
"SurfaceKind",
"SurfaceScope",
"Switch",
"SwitchRow",
"TOTPDialog",
"TabStrip",
"TabStripItem",
"TagInput",
"TextAreaField",
"TextField",
"Textarea",
"TextareaProps",
"Tooltip",
@@ -167,7 +206,9 @@ const ALLOWED = [
"TooltipProvider",
"TooltipTrigger",
"UptimeInfo",
"UsagePair",
"VersionInfo",
"ViewToggle",
"WebSocketConnectionTarget",
"WidgetTitle",
"buildOriginWsUrl",
@@ -198,13 +239,17 @@ const ALLOWED = [
"runAdaptiveBackgroundTask",
"runAdaptivePolling",
"runVisibleInterval",
"usageBarColor",
"usageColor",
"useAdaptivePolling",
"useAppTheme",
"useConfirmation",
"useConfirm",
"useConnectionGate",
"useConnectionLog",
"useIsDefaultsEditor",
"useIsMobile",
"useOptionalConnectionLog",
"useSurfaceKind",
"useTabs",
"useTabsSafe",
];
+140
View File
@@ -0,0 +1,140 @@
#!/usr/bin/env node
/**
* Builds src/ui/settings/settings-search-index.ts: the i18n keys each core
* settings section shows, so the Settings search can match on any label in
* a section without mounting it. Run it after changing a settings section.
*
* --check fails when the committed file is out of date.
*/
const fs = require("node:fs");
const path = require("node:path");
const ROOT = path.resolve(__dirname, "..");
const SIDEBAR = path.join(ROOT, "src", "ui", "sidebar");
const OUT = path.join(
ROOT,
"src",
"ui",
"settings",
"settings-search-index.ts",
);
function keysIn(text) {
return [
...new Set([...text.matchAll(/\bt\(\s*"([\w.-]+)"/g)].map((m) => m[1])),
];
}
function read(file) {
return fs.readFileSync(path.join(SIDEBAR, file), "utf8");
}
/** The body of one exported or local function, by name. */
function functionBody(source, name) {
const start = source.search(new RegExp(`function ${name}\\b`));
if (start < 0) throw new Error(`No function ${name}`);
const next = source.slice(start + 1).search(/\n(export )?function \w+/);
return next < 0 ? source.slice(start) : source.slice(start, start + 1 + next);
}
/** Profile sections, split on their AccordionSection ids. */
function profileSections() {
const source = read("UserProfilePanel.tsx");
const lines = source.split("\n");
const starts = [];
lines.forEach((line, i) => {
if (/^\s{6}<AccordionSection\b/.test(line)) starts.push(i);
});
const out = {};
starts.forEach((start, k) => {
const end = k + 1 < starts.length ? starts[k + 1] : lines.length;
const chunk = lines.slice(start, end).join("\n");
const id = (chunk.match(/\bid="([^"]+)"/) || [])[1];
if (id) out[id] = keysIn(chunk);
});
out.interface = [
...new Set([
...(out.interface ?? []),
...keysIn(read("InterfacePresetSettings.tsx")),
]),
];
out.security = [
...new Set([
...(out.security ?? []),
...keysIn(read("AuthEnrollmentSections.tsx")),
...keysIn(functionBody(source, "PasswordChangeSection")),
]),
];
out["api-keys"] = [
...new Set([
...(out["api-keys"] ?? []),
...keysIn(functionBody(source, "NewApiKeyDialog")),
]),
];
out.keybindings = keysIn(
fs.readFileSync(
path.join(ROOT, "src", "ui", "settings", "KeybindingsSettings.tsx"),
"utf8",
),
);
return out;
}
function adminSections() {
const settings = read("AdminSettingsSections.tsx");
const management = read("AdminManagementSections.tsx");
return {
"admin-general": keysIn(
functionBody(settings, "AdminGeneralSettingsSection"),
),
"admin-users": [
...new Set([
...keysIn(functionBody(management, "AdminUsersSection")),
...keysIn(read("AdminUserDialogs.tsx")),
]),
],
"admin-sessions": keysIn(functionBody(management, "AdminSessionsSection")),
"admin-roles": keysIn(functionBody(management, "AdminRolesSection")),
"admin-host-defaults": [
...new Set([
...keysIn(functionBody(settings, "AdminHostDefaultsSection")),
...keysIn(read("AdminHostTags.tsx")),
]),
],
"admin-branding": keysIn(read("AdminBrandingSection.tsx")),
"admin-database": keysIn(functionBody(settings, "AdminDatabaseSection")),
"admin-ssl": keysIn(functionBody(settings, "AdminSSLSection")),
"admin-api-keys": keysIn(read("AdminApiKeysSection.tsx")),
"admin-audit-log": keysIn(read("AdminAuditLogSection.tsx")),
};
}
function render() {
const index = { ...profileSections(), ...adminSections() };
const body = Object.entries(index)
.sort(([a], [b]) => a.localeCompare(b))
.map(
([id, keys]) =>
` ${JSON.stringify(id)}: [\n${keys
.sort()
.map((key) => ` ${JSON.stringify(key)},`)
.join("\n")}\n ],`,
)
.join("\n");
return `// Generated by scripts/generate-settings-search-index.cjs. Do not edit.\n\n/** The i18n keys each core settings section shows, for search. */\nexport const SETTINGS_SEARCH_INDEX: Record<string, string[]> = {\n${body}\n};\n`;
}
const next = render();
if (process.argv.includes("--check")) {
const current = fs.existsSync(OUT) ? fs.readFileSync(OUT, "utf8") : "";
if (current !== next) {
console.error(
"src/ui/settings/settings-search-index.ts is out of date. Run: node scripts/generate-settings-search-index.cjs",
);
process.exit(1);
}
} else {
fs.writeFileSync(OUT, next);
console.log(`Wrote ${path.relative(ROOT, OUT)}`);
}