@agimon-ai/po-ui 0.0.0-alpha.2 → 0.0.0-alpha.21
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/LICENSE +149 -21
- package/README.md +1 -1
- package/dist/apiContracts.cjs +1 -1
- package/dist/apiContracts.cjs.map +1 -1
- package/dist/apiContracts.d.mts +1 -1
- package/dist/apiContracts.mjs +1 -1
- package/dist/apiContracts.mjs.map +1 -1
- package/dist/config.d.mts +2 -2
- package/dist/config.mjs +1 -2
- package/dist/extensionName.cjs +66 -16
- package/dist/extensionName.cjs.map +1 -1
- package/dist/extensionName.d.mts +13 -1
- package/dist/extensionName.d.mts.map +1 -1
- package/dist/extensionName.mjs +64 -16
- package/dist/extensionName.mjs.map +1 -1
- package/dist/extensions/mcp-ui.mjs.map +1 -1
- package/dist/extensions/mcp.cjs +3 -3
- package/dist/extensions/mcp.cjs.map +1 -1
- package/dist/extensions/mcp.d.mts +1 -1
- package/dist/extensions/mcp.mjs +4 -4
- package/dist/extensions/mcp.mjs.map +1 -1
- package/dist/extensions/server.cjs +7 -7
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.d.mts +2 -2
- package/dist/extensions/server.d.mts.map +1 -1
- package/dist/extensions/server.mjs +7 -7
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/extensions/web.mjs +11 -11
- package/dist/extensions/web.mjs.map +1 -1
- package/dist/footer.d.mts +2 -2
- package/dist/footer.mjs +1 -2
- package/dist/{harnessMetadata-Dw9Rb1nW.cjs → harnessMetadata-B3mBGrS3.cjs} +4 -4
- package/dist/harnessMetadata-B3mBGrS3.cjs.map +1 -0
- package/dist/{harnessMetadata-CzsdGAxL.d.mts → harnessMetadata-BzMgOB0W.d.mts} +4 -4
- package/dist/harnessMetadata-BzMgOB0W.d.mts.map +1 -0
- package/dist/{harnessMetadata-DkMSJutN.mjs → harnessMetadata-Ccv_j7WP.mjs} +3 -3
- package/dist/harnessMetadata-Ccv_j7WP.mjs.map +1 -0
- package/dist/harnessMetadata.cjs +2 -2
- package/dist/harnessMetadata.d.mts +2 -2
- package/dist/harnessMetadata.mjs +1 -2
- package/dist/{index-Bx49wmE7.d.mts → index-2GW2dj0I.d.mts} +9 -9
- package/dist/{index-Bx49wmE7.d.mts.map → index-2GW2dj0I.d.mts.map} +1 -1
- package/dist/{index-Dpzvl-vg.d.mts → index-DFSkSBBh.d.mts} +19 -11
- package/dist/index-DFSkSBBh.d.mts.map +1 -0
- package/dist/index.cjs +6 -6
- package/dist/index.d.mts +8 -8
- package/dist/index.mjs +5 -5
- package/dist/leader.d.mts +2 -2
- package/dist/leaderRegistry.cjs +2 -2
- package/dist/leaderRegistry.cjs.map +1 -1
- package/dist/leaderRegistry.d.mts +15 -15
- package/dist/leaderRegistry.d.mts.map +1 -1
- package/dist/leaderRegistry.mjs +2 -2
- package/dist/leaderRegistry.mjs.map +1 -1
- package/dist/logSinkTelemetry.cjs +1 -1
- package/dist/logSinkTelemetry.d.mts +1 -1
- package/dist/logSinkTelemetry.mjs +1 -2
- package/dist/skills.cjs +8 -8
- package/dist/skills.d.mts +2 -2
- package/dist/skills.mjs +1 -2
- package/dist/{telemetry-DMR6cIT1.cjs → telemetry-EXlQc9s0.cjs} +8 -8
- package/dist/telemetry-EXlQc9s0.cjs.map +1 -0
- package/dist/{telemetry--EgKB9ei.mjs → telemetry-RiFqd0cg.mjs} +9 -9
- package/dist/telemetry-RiFqd0cg.mjs.map +1 -0
- package/dist/{theme-Byj_dBlT.mjs → theme-BX_hfErv.mjs} +60 -40
- package/dist/theme-BX_hfErv.mjs.map +1 -0
- package/dist/{theme-2wveit5V.cjs → theme-BfC_GGQ4.cjs} +66 -40
- package/dist/theme-BfC_GGQ4.cjs.map +1 -0
- package/dist/theme.cjs +2 -1
- package/dist/theme.d.mts +2 -2
- package/dist/theme.mjs +1 -2
- package/dist/toolInventory.mjs +1 -2
- package/dist/{uiState-G_Pso-5i.mjs → uiState-C7I5WcMl.mjs} +3 -3
- package/dist/uiState-C7I5WcMl.mjs.map +1 -0
- package/dist/{uiState-rUwgKslb.cjs → uiState-CCytmxyX.cjs} +4 -4
- package/dist/uiState-CCytmxyX.cjs.map +1 -0
- package/dist/{uiState-Binu4nUb.d.mts → uiState-DbjaVyxl.d.mts} +3 -3
- package/dist/{uiState-Binu4nUb.d.mts.map → uiState-DbjaVyxl.d.mts.map} +1 -1
- package/dist/uiState.cjs +2 -2
- package/dist/uiState.d.mts +2 -2
- package/dist/uiState.mjs +1 -2
- package/package.json +19 -19
- package/dist/harnessMetadata-CzsdGAxL.d.mts.map +0 -1
- package/dist/harnessMetadata-DkMSJutN.mjs.map +0 -1
- package/dist/harnessMetadata-Dw9Rb1nW.cjs.map +0 -1
- package/dist/index-Dpzvl-vg.d.mts.map +0 -1
- package/dist/telemetry--EgKB9ei.mjs.map +0 -1
- package/dist/telemetry-DMR6cIT1.cjs.map +0 -1
- package/dist/theme-2wveit5V.cjs.map +0 -1
- package/dist/theme-Byj_dBlT.mjs.map +0 -1
- package/dist/uiState-G_Pso-5i.mjs.map +0 -1
- package/dist/uiState-rUwgKslb.cjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"leaderRegistry.mjs","names":[],"sources":["../src/services/leaderRegistry/index.ts"],"sourcesContent":["type AppKeybinding =\n | 'app.model.select'\n | 'app.model.cycleForward'\n | 'app.thinking.cycle'\n | 'app.session.new'\n | 'app.session.resume'\n | 'app.session.tree'\n | 'app.session.fork'\n | 'app.editor.external'\n | 'app.exit';\n\nimport type {\n LeaderBinding as DoomLeaderBinding,\n LeaderCommand as DoomLeaderCommand,\n LeaderContribution as DoomLeaderContribution,\n LeaderAction as DoomLeaderExtensionAction,\n LeaderSegment as DoomLeaderSegment,\n LeaderTone as DoomLeaderTone,\n} from '@agimon-ai/po-core/leader';\n\ninterface AppLeaderAction {\n type: 'app';\n action: AppKeybinding;\n}\n\ninterface CommandLeaderAction {\n type: 'command';\n command: DoomLeaderCommand;\n}\n\ninterface ExtensionLeaderAction {\n type: 'extension';\n source: string;\n action: DoomLeaderExtensionAction;\n}\n\nexport type DoomLeaderAction = AppLeaderAction | CommandLeaderAction | ExtensionLeaderAction;\n\nexport interface DoomLeaderResolvedOption {\n key: string;\n label: string;\n detail?: string;\n tone?: DoomLeaderTone;\n hasChildren: boolean;\n action?: DoomLeaderAction;\n}\n\nexport interface DoomLeaderGroup {\n label: string;\n options: readonly DoomLeaderResolvedOption[];\n}\n\nexport interface DoomLeaderDiagnostic {\n source: string;\n bindingId?: string;\n message: string;\n}\n\nexport interface DoomLeaderRegistrationResult {\n accepted: boolean;\n diagnostics: readonly DoomLeaderDiagnostic[];\n}\n\ninterface InternalLeaderBinding {\n id: string;\n source: string;\n path: readonly [DoomLeaderSegment, ...DoomLeaderSegment[]];\n action: DoomLeaderAction;\n}\n\ninterface LeaderNode {\n key: string;\n label: string;\n detail?: string;\n tone?: DoomLeaderTone;\n order: number;\n owner: string;\n bindingId?: string;\n children: Map<string, LeaderNode>;\n action?: DoomLeaderAction;\n}\n\ninterface ParsedContribution {\n contribution?: DoomLeaderContribution;\n diagnostics: DoomLeaderDiagnostic[];\n}\n\nconst CORE_SOURCE = '@agimon-ai/po-ui';\nconst UNKNOWN_SOURCE = 'unknown';\nconst ROOT_LABEL = 'leader';\nconst DEFAULT_OPTION_ORDER = 500;\nconst MAX_OPTION_ORDER = 1000;\nconst MAX_PATH_LENGTH = 4;\nconst MAX_SOURCE_LENGTH = 128;\nconst MAX_BINDING_ID_LENGTH = 128;\nconst MAX_LABEL_LENGTH = 32;\nconst MAX_DETAIL_LENGTH = 80;\nconst MAX_COMMAND_NAME_LENGTH = 64;\nconst MAX_COMMAND_ARGUMENT_LENGTH = 256;\nconst MAX_ACTION_NAME_LENGTH = 128;\nconst SAFE_KEY = /^[a-z0-9]$/;\nconst SAFE_IDENTIFIER = /^[a-z0-9][a-z0-9._:-]*$/;\nconst SAFE_SOURCE = /^[A-Za-z0-9@][A-Za-z0-9@/._:-]*$/;\nconst SAFE_COMMAND_NAME = /^[a-z0-9][a-z0-9:_-]*$/;\nconst C0_CONTROL_MAX = 31;\nconst DELETE_CHARACTER = 127;\nconst MODEL_GROUP = { key: 'm', label: 'models', detail: 'model and thinking level', order: 10 } as const;\nconst SESSION_GROUP = { key: 's', label: 'sessions', detail: 'history, forks and branches', order: 30 } as const;\nconst EXTENSION_GROUP = { key: 'e', label: 'extension', detail: 'tools, skills and config', order: 50 } as const;\nconst HELP_GROUP = { key: 'h', label: 'help', detail: 'package docs and logs', order: 70 } as const;\nconst QUIT_GROUP = { key: 'q', label: 'quit', detail: 'leave the session', order: 90 } as const;\n\nfunction coreBinding(\n id: string,\n path: readonly [DoomLeaderSegment, ...DoomLeaderSegment[]],\n action: DoomLeaderAction,\n): InternalLeaderBinding {\n return { id, source: CORE_SOURCE, path, action };\n}\n\nconst CORE_BINDINGS: readonly InternalLeaderBinding[] = [\n coreBinding('model.select', [MODEL_GROUP, { key: 'm', label: 'select', detail: 'choose model', order: 10 }], {\n type: 'app',\n action: 'app.model.select',\n }),\n coreBinding('model.next', [MODEL_GROUP, { key: 'n', label: 'next', detail: 'cycle model', order: 20 }], {\n type: 'app',\n action: 'app.model.cycleForward',\n }),\n coreBinding('thinking.cycle', [MODEL_GROUP, { key: 't', label: 'thinking', detail: 'cycle level', order: 30 }], {\n type: 'app',\n action: 'app.thinking.cycle',\n }),\n coreBinding('tools.open', [EXTENSION_GROUP, { key: 't', label: 'tools', detail: 'browse tools', order: 20 }], {\n type: 'command',\n command: { name: 'tools' },\n }),\n // Core rather than contributed, so no extension owns the key and the panel is\n // reachable even when nothing has contributed a section to it yet.\n coreBinding('config.open', [EXTENSION_GROUP, { key: 'c', label: 'config', detail: 'settings', order: 30 }], {\n type: 'command',\n command: { name: 'config' },\n }),\n coreBinding('session.new', [SESSION_GROUP, { key: 'n', label: 'new', detail: 'fresh session', order: 10 }], {\n type: 'app',\n action: 'app.session.new',\n }),\n coreBinding('session.resume', [SESSION_GROUP, { key: 'r', label: 'resume', detail: 'open history', order: 20 }], {\n type: 'app',\n action: 'app.session.resume',\n }),\n coreBinding('session.tree', [SESSION_GROUP, { key: 't', label: 'tree', detail: 'branch view', order: 30 }], {\n type: 'app',\n action: 'app.session.tree',\n }),\n coreBinding('session.fork', [SESSION_GROUP, { key: 'f', label: 'fork', detail: 'from selection', order: 40 }], {\n type: 'app',\n action: 'app.session.fork',\n }),\n coreBinding('help.hotkeys', [HELP_GROUP, { key: 'h', label: 'hotkeys', detail: 'show bindings' }], {\n type: 'command',\n command: { name: 'hotkeys' },\n }),\n coreBinding('app.exit', [QUIT_GROUP, { key: 'q', label: 'quit', detail: 'exit Pi' }], {\n type: 'app',\n action: 'app.exit',\n }),\n];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction diagnostic(source: string, message: string, bindingId?: string): DoomLeaderDiagnostic {\n return { source, message, ...(bindingId ? { bindingId } : {}) };\n}\n\nfunction containsControlCharacter(value: string): boolean {\n for (let index = 0; index < value.length; index += 1) {\n const codeUnit = value.charCodeAt(index);\n if (codeUnit <= C0_CONTROL_MAX || codeUnit === DELETE_CHARACTER) return true;\n }\n return false;\n}\n\nfunction readRequiredText(\n value: unknown,\n field: string,\n maximumLength: number,\n source: string,\n diagnostics: DoomLeaderDiagnostic[],\n bindingId?: string,\n): string | undefined {\n if (typeof value !== 'string' || !value.trim()) {\n diagnostics.push(diagnostic(source, `${field} must be a non-empty string.`, bindingId));\n return undefined;\n }\n const text = value.trim();\n if (text.length > maximumLength || containsControlCharacter(text)) {\n diagnostics.push(\n diagnostic(\n source,\n `${field} must be at most ${maximumLength} characters and contain no control characters.`,\n bindingId,\n ),\n );\n return undefined;\n }\n return text;\n}\n\nfunction readOptionalText(\n value: unknown,\n field: string,\n maximumLength: number,\n source: string,\n diagnostics: DoomLeaderDiagnostic[],\n bindingId: string,\n): string | undefined {\n if (value === undefined) return undefined;\n return readRequiredText(value, field, maximumLength, source, diagnostics, bindingId);\n}\n\nfunction parseSegment(\n value: unknown,\n source: string,\n bindingId: string,\n index: number,\n diagnostics: DoomLeaderDiagnostic[],\n): DoomLeaderSegment | undefined {\n if (!isRecord(value)) {\n diagnostics.push(diagnostic(source, `bindings[].path[${index}] must be an object.`, bindingId));\n return undefined;\n }\n\n const key = readRequiredText(value.key, `bindings[].path[${index}].key`, 1, source, diagnostics, bindingId);\n const label = readRequiredText(\n value.label,\n `bindings[].path[${index}].label`,\n MAX_LABEL_LENGTH,\n source,\n diagnostics,\n bindingId,\n );\n const detail = readOptionalText(\n value.detail,\n `bindings[].path[${index}].detail`,\n MAX_DETAIL_LENGTH,\n source,\n diagnostics,\n bindingId,\n );\n // An unrecognised tone is ignored, not diagnosed. Every other malformed field\n // here rejects the whole contribution, which is right for a key or a label but\n // not for a badge colour: it would mean a package compiled against a later\n // contract loses its entire menu on an older host over a cosmetic value. The\n // union type already catches a typo at every call site built against this\n // contract, which is the only place a typo can come from.\n const tone: DoomLeaderTone | undefined = value.tone === 'default' || value.tone === 'exit' ? value.tone : undefined;\n let order: number | undefined;\n if (value.order !== undefined) {\n if (!Number.isInteger(value.order) || (value.order as number) < 0 || (value.order as number) > MAX_OPTION_ORDER) {\n diagnostics.push(\n diagnostic(\n source,\n `bindings[].path[${index}].order must be an integer from 0 to ${MAX_OPTION_ORDER}.`,\n bindingId,\n ),\n );\n } else {\n order = value.order as number;\n }\n }\n if (!key || !label || !SAFE_KEY.test(key)) {\n if (key && !SAFE_KEY.test(key)) {\n diagnostics.push(diagnostic(source, `bindings[].path[${index}].key must match ${SAFE_KEY}.`, bindingId));\n }\n return undefined;\n }\n return {\n key,\n label,\n ...(detail ? { detail } : {}),\n ...(tone ? { tone } : {}),\n ...(order === undefined ? {} : { order }),\n };\n}\n\nfunction parseCommand(\n value: unknown,\n source: string,\n bindingId: string,\n diagnostics: DoomLeaderDiagnostic[],\n): DoomLeaderCommand | undefined {\n if (!isRecord(value)) {\n diagnostics.push(diagnostic(source, 'bindings[].command must be an object.', bindingId));\n return undefined;\n }\n const name = readRequiredText(\n value.name,\n 'bindings[].command.name',\n MAX_COMMAND_NAME_LENGTH,\n source,\n diagnostics,\n bindingId,\n );\n const args = readOptionalText(\n value.args,\n 'bindings[].command.args',\n MAX_COMMAND_ARGUMENT_LENGTH,\n source,\n diagnostics,\n bindingId,\n );\n if (!name || !SAFE_COMMAND_NAME.test(name)) {\n if (name && !SAFE_COMMAND_NAME.test(name)) {\n diagnostics.push(diagnostic(source, `bindings[].command.name must match ${SAFE_COMMAND_NAME}.`, bindingId));\n }\n return undefined;\n }\n return { name, ...(args ? { args } : {}) };\n}\n\nfunction parseExtensionAction(\n value: unknown,\n source: string,\n bindingId: string,\n diagnostics: DoomLeaderDiagnostic[],\n): DoomLeaderExtensionAction | undefined {\n if (!isRecord(value)) {\n diagnostics.push(diagnostic(source, 'bindings[].action must be an object.', bindingId));\n return undefined;\n }\n const name = readRequiredText(\n value.name,\n 'bindings[].action.name',\n MAX_ACTION_NAME_LENGTH,\n source,\n diagnostics,\n bindingId,\n );\n if (!name || !SAFE_IDENTIFIER.test(name)) {\n if (name && !SAFE_IDENTIFIER.test(name)) {\n diagnostics.push(diagnostic(source, `bindings[].action.name must match ${SAFE_IDENTIFIER}.`, bindingId));\n }\n return undefined;\n }\n return { name };\n}\n\nfunction parseBinding(\n value: unknown,\n source: string,\n diagnostics: DoomLeaderDiagnostic[],\n): DoomLeaderBinding | undefined {\n if (!isRecord(value)) {\n diagnostics.push(diagnostic(source, 'bindings[] must be an object.'));\n return undefined;\n }\n const bindingId = readRequiredText(value.id, 'bindings[].id', MAX_BINDING_ID_LENGTH, source, diagnostics);\n if (!bindingId) return undefined;\n if (!SAFE_IDENTIFIER.test(bindingId)) {\n diagnostics.push(diagnostic(source, `bindings[].id must match ${SAFE_IDENTIFIER}.`, bindingId));\n return undefined;\n }\n if (!Array.isArray(value.path) || value.path.length === 0 || value.path.length > MAX_PATH_LENGTH) {\n diagnostics.push(\n diagnostic(source, `bindings[].path must contain between 1 and ${MAX_PATH_LENGTH} segments.`, bindingId),\n );\n return undefined;\n }\n\n const path = value.path.map((segment, index) => parseSegment(segment, source, bindingId, index, diagnostics));\n const hasCommand = Object.hasOwn(value, 'command');\n const hasAction = Object.hasOwn(value, 'action');\n if (hasCommand === hasAction) {\n diagnostics.push(diagnostic(source, 'bindings[] must contain exactly one of command or action.', bindingId));\n return undefined;\n }\n const target = hasCommand\n ? { command: parseCommand(value.command, source, bindingId, diagnostics) }\n : { action: parseExtensionAction(value.action, source, bindingId, diagnostics) };\n if (path.some((segment) => segment === undefined)) return undefined;\n if ('command' in target) {\n if (!target.command) return undefined;\n return {\n id: bindingId,\n path: path as [DoomLeaderSegment, ...DoomLeaderSegment[]],\n command: target.command,\n };\n }\n if (!target.action) return undefined;\n return {\n id: bindingId,\n path: path as [DoomLeaderSegment, ...DoomLeaderSegment[]],\n action: target.action,\n };\n}\n\nfunction parseContribution(value: unknown): ParsedContribution {\n const diagnostics: DoomLeaderDiagnostic[] = [];\n if (!isRecord(value)) {\n return { diagnostics: [diagnostic(UNKNOWN_SOURCE, 'Leader contribution must be an object.')] };\n }\n const source = readRequiredText(value.source, 'source', MAX_SOURCE_LENGTH, UNKNOWN_SOURCE, diagnostics);\n if (!source) return { diagnostics };\n if (!SAFE_SOURCE.test(source)) {\n diagnostics.push(diagnostic(source, `source must match ${SAFE_SOURCE}.`));\n return { diagnostics };\n }\n if (!Array.isArray(value.bindings)) {\n return { diagnostics: [diagnostic(source, 'bindings must be an array.')] };\n }\n\n const bindings = value.bindings.map((binding) => parseBinding(binding, source, diagnostics));\n const duplicateIds = bindings\n .flatMap((binding) => (binding ? [binding.id] : []))\n .filter((id, index, ids) => ids.indexOf(id) !== index);\n for (const id of new Set(duplicateIds)) {\n diagnostics.push(diagnostic(source, `Binding id '${id}' is duplicated.`, id));\n }\n if (diagnostics.length > 0 || bindings.some((binding) => binding === undefined)) return { diagnostics };\n\n return {\n contribution: {\n source,\n bindings: bindings as DoomLeaderBinding[],\n },\n diagnostics,\n };\n}\n\nfunction optionOrder(segment: DoomLeaderSegment): number {\n return segment.order ?? DEFAULT_OPTION_ORDER;\n}\n\nfunction cloneAction(action: DoomLeaderAction): DoomLeaderAction {\n if (action.type === 'command') return { type: 'command', command: { ...action.command } };\n if (action.type === 'app') return { type: 'app', action: action.action };\n return { type: 'extension', source: action.source, action: { ...action.action } };\n}\n\nfunction createRootNode(): LeaderNode {\n return {\n key: '',\n label: ROOT_LABEL,\n order: 0,\n owner: CORE_SOURCE,\n children: new Map(),\n };\n}\n\nfunction pathLabel(path: readonly DoomLeaderSegment[]): string {\n return `SPC ${path.map((segment) => segment.key).join(' ')}`;\n}\n\n/**\n * Whether a shared group prefix is worded the same way by both contributors.\n *\n * `detail` is left out on purpose: it is a subtitle, and these packages ship on\n * independent npm versions, so comparing it means one upgraded package disagrees\n * with every package that has not caught up.\n *\n * A mismatch is REPORTED, never fatal. Losing `SPC e s` because two packages\n * spell the `SPC e` group differently costs the user a key they can no longer\n * press, which is worse than a menu row worded by whoever registered first.\n */\nfunction metadataMatches(node: LeaderNode, segment: DoomLeaderSegment): boolean {\n return node.label === segment.label && node.order === optionOrder(segment);\n}\n\n/** Every binding under a node, for a takeover message that says what it displaced. */\nfunction leafCount(node: LeaderNode): number {\n if (node.children.size === 0) return node.action ? 1 : 0;\n let total = 0;\n for (const child of node.children.values()) total += leafCount(child);\n return total;\n}\n\n/**\n * Inserts one binding, taking a key over from whoever holds it.\n *\n * LAST REGISTRATION WINS, and the diagnostics say who lost what. The rule this\n * replaced rejected the newcomer, which read as \"warning plus nothing happened\":\n * the key stayed bound to the first registrant, or - when the clash was only a\n * group label - to nobody at all. A takeover is recoverable in a way a dropped\n * binding is not: the tree is rebuilt from every stored contribution, so when\n * the winner unregisters, the displaced binding comes back on the next rebuild.\n *\n * Returns diagnostics rather than one issue, because a single insert can both\n * disagree about a group's wording and take a key from someone.\n */\nfunction insertBinding(root: LeaderNode, binding: InternalLeaderBinding): DoomLeaderDiagnostic[] {\n const diagnostics: DoomLeaderDiagnostic[] = [];\n let current = root;\n let firstMissing = -1;\n\n for (const [index, segment] of binding.path.entries()) {\n const existing = current.children.get(segment.key);\n if (!existing) {\n firstMissing = index;\n break;\n }\n if (!metadataMatches(existing, segment)) {\n diagnostics.push(\n diagnostic(\n binding.source,\n `${pathLabel(binding.path.slice(0, index + 1))} keeps the label from ${existing.owner}; the binding is registered.`,\n binding.id,\n ),\n );\n }\n // First writer wins for the subtitle, but a group created without one still\n // adopts the first that is offered, so the wording does not depend on which\n // package happened to register first.\n if (!existing.detail && segment.detail) existing.detail = segment.detail;\n\n const isLeaf = index === binding.path.length - 1;\n const displaced = leafCount(existing);\n if (isLeaf && displaced > 0) {\n diagnostics.push(\n diagnostic(\n binding.source,\n `${pathLabel(binding.path)} taken over from ${existing.owner}` +\n `${displaced > 1 ? `, displacing ${displaced} bindings` : ''}.`,\n binding.id,\n ),\n );\n existing.children.clear();\n delete existing.action;\n delete existing.bindingId;\n } else if (!isLeaf && existing.action) {\n diagnostics.push(\n diagnostic(\n binding.source,\n `${pathLabel(binding.path.slice(0, index + 1))} taken over from ${existing.owner} to open a group.`,\n binding.id,\n ),\n );\n delete existing.action;\n delete existing.bindingId;\n }\n if (isLeaf || existing.action === undefined) existing.owner = binding.source;\n current = existing;\n }\n\n const start = firstMissing === -1 ? binding.path.length : firstMissing;\n for (let index = start; index < binding.path.length; index += 1) {\n const segment = binding.path[index] as DoomLeaderSegment;\n const node: LeaderNode = {\n key: segment.key,\n label: segment.label,\n ...(segment.detail ? { detail: segment.detail } : {}),\n ...(segment.tone ? { tone: segment.tone } : {}),\n order: optionOrder(segment),\n owner: binding.source,\n children: new Map(),\n };\n current.children.set(segment.key, node);\n current = node;\n }\n\n current.action = cloneAction(binding.action);\n current.bindingId = binding.id;\n current.owner = binding.source;\n return diagnostics;\n}\n\nexport class DoomLeaderRegistry {\n private root = createRootNode();\n private readonly contributions = new Map<string, readonly DoomLeaderBinding[]>();\n private readonly listeners = new Set<() => void>();\n private diagnostics: DoomLeaderDiagnostic[] = [];\n private dirty = false;\n\n constructor() {\n this.rebuild();\n }\n\n register(value: unknown): DoomLeaderRegistrationResult {\n const result = this.apply(value);\n if (!result.accepted) return result;\n this.rebuild();\n return { accepted: true, diagnostics: this.getDiagnostics() };\n }\n\n /** Records a contribution without rebuilding the whole tree immediately. */\n registerDeferred(value: unknown): DoomLeaderRegistrationResult {\n return this.apply(value);\n }\n\n private apply(value: unknown): DoomLeaderRegistrationResult {\n const parsed = parseContribution(value);\n if (!parsed.contribution) return { accepted: false, diagnostics: parsed.diagnostics };\n\n // Deleted before it is set again, so re-registering moves a source to the\n // back of the insertion order: registering again IS claiming again.\n this.contributions.delete(parsed.contribution.source);\n if (parsed.contribution.bindings.length > 0) {\n this.contributions.set(parsed.contribution.source, parsed.contribution.bindings);\n }\n this.dirty = true;\n return { accepted: true, diagnostics: parsed.diagnostics };\n }\n\n /** Rebuilds one batch and returns its deterministic conflict diagnostics. */\n flush(): DoomLeaderDiagnostic[] {\n if (this.dirty) this.rebuild();\n return this.diagnostics.map((item) => ({ ...item }));\n }\n\n getGroup(path: readonly string[]): DoomLeaderGroup | undefined {\n this.flush();\n let current = this.root;\n for (const key of path) {\n const next = current.children.get(key);\n if (!next || next.action) return undefined;\n current = next;\n }\n\n // The key is the sort, at every level. A reader looking for a chord knows the\n // key, not the number some contributor picked for it, and those numbers live\n // across nine packages so no one of them can see the sequence they produce.\n // `order` survives only as group identity: co-contributors to a shared prefix\n // must still agree on it, which is what `metadataMatches` checks.\n const options = [...current.children.values()]\n .sort((left, right) => left.key.localeCompare(right.key))\n .map((node): DoomLeaderResolvedOption => ({\n key: node.key,\n label: node.label,\n ...(node.detail ? { detail: node.detail } : {}),\n ...(node.tone ? { tone: node.tone } : {}),\n hasChildren: node.children.size > 0,\n ...(node.action ? { action: cloneAction(node.action) } : {}),\n }));\n return { label: current.label, options };\n }\n\n getDiagnostics(): DoomLeaderDiagnostic[] {\n return this.flush();\n }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private rebuild(): void {\n const nextRoot = createRootNode();\n const diagnostics: DoomLeaderDiagnostic[] = [];\n for (const binding of CORE_BINDINGS) {\n // Core bindings land in an empty tree, so anything reported here is two\n // core bindings disagreeing with each other: a defect, not a contribution.\n const [issue] = insertBinding(nextRoot, binding);\n if (issue) throw new Error(`Invalid core leader binding: ${issue.message}`);\n }\n\n // Registration order, not alphabetical: the key belongs to whoever claimed\n // it last, and `apply` re-seats a source that registers again so a reload\n // counts as a fresh claim. Bindings inside one contribution stay sorted by\n // id, so a single source's own output is still deterministic.\n for (const [source, bindings] of this.contributions.entries()) {\n for (const binding of [...bindings].sort((left, right) => left.id.localeCompare(right.id))) {\n diagnostics.push(\n ...insertBinding(nextRoot, {\n id: binding.id,\n source,\n path: binding.path,\n action:\n 'command' in binding\n ? { type: 'command', command: binding.command }\n : { type: 'extension', source, action: binding.action },\n }),\n );\n }\n }\n\n this.root = nextRoot;\n this.diagnostics = diagnostics;\n this.dirty = false;\n for (const listener of this.listeners) listener();\n }\n}\n"],"mappings":";AAuFA,MAAM,cAAc;AACpB,MAAM,iBAAiB;AACvB,MAAM,aAAa;AACnB,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AACzB,MAAM,kBAAkB;AACxB,MAAM,oBAAoB;AAC1B,MAAM,wBAAwB;AAC9B,MAAM,mBAAmB;AACzB,MAAM,oBAAoB;AAC1B,MAAM,0BAA0B;AAChC,MAAM,8BAA8B;AACpC,MAAM,yBAAyB;AAC/B,MAAM,WAAW;AACjB,MAAM,kBAAkB;AACxB,MAAM,cAAc;AACpB,MAAM,oBAAoB;AAC1B,MAAM,iBAAiB;AACvB,MAAM,mBAAmB;AACzB,MAAM,cAAc;CAAE,KAAK;CAAK,OAAO;CAAU,QAAQ;CAA4B,OAAO;AAAG;AAC/F,MAAM,gBAAgB;CAAE,KAAK;CAAK,OAAO;CAAY,QAAQ;CAA+B,OAAO;AAAG;AACtG,MAAM,kBAAkB;CAAE,KAAK;CAAK,OAAO;CAAa,QAAQ;CAA4B,OAAO;AAAG;AACtG,MAAM,aAAa;CAAE,KAAK;CAAK,OAAO;CAAQ,QAAQ;CAAyB,OAAO;AAAG;AACzF,MAAM,aAAa;CAAE,KAAK;CAAK,OAAO;CAAQ,QAAQ;CAAqB,OAAO;AAAG;AAErF,SAAS,YACP,IACA,MACA,QACuB;CACvB,OAAO;EAAE;EAAI,QAAQ;EAAa;EAAM;CAAO;AACjD;AAEA,MAAM,gBAAkD;CACtD,YAAY,gBAAgB,CAAC,aAAa;EAAE,KAAK;EAAK,OAAO;EAAU,QAAQ;EAAgB,OAAO;CAAG,CAAC,GAAG;EAC3G,MAAM;EACN,QAAQ;CACV,CAAC;CACD,YAAY,cAAc,CAAC,aAAa;EAAE,KAAK;EAAK,OAAO;EAAQ,QAAQ;EAAe,OAAO;CAAG,CAAC,GAAG;EACtG,MAAM;EACN,QAAQ;CACV,CAAC;CACD,YAAY,kBAAkB,CAAC,aAAa;EAAE,KAAK;EAAK,OAAO;EAAY,QAAQ;EAAe,OAAO;CAAG,CAAC,GAAG;EAC9G,MAAM;EACN,QAAQ;CACV,CAAC;CACD,YAAY,cAAc,CAAC,iBAAiB;EAAE,KAAK;EAAK,OAAO;EAAS,QAAQ;EAAgB,OAAO;CAAG,CAAC,GAAG;EAC5G,MAAM;EACN,SAAS,EAAE,MAAM,QAAQ;CAC3B,CAAC;CAGD,YAAY,eAAe,CAAC,iBAAiB;EAAE,KAAK;EAAK,OAAO;EAAU,QAAQ;EAAY,OAAO;CAAG,CAAC,GAAG;EAC1G,MAAM;EACN,SAAS,EAAE,MAAM,SAAS;CAC5B,CAAC;CACD,YAAY,eAAe,CAAC,eAAe;EAAE,KAAK;EAAK,OAAO;EAAO,QAAQ;EAAiB,OAAO;CAAG,CAAC,GAAG;EAC1G,MAAM;EACN,QAAQ;CACV,CAAC;CACD,YAAY,kBAAkB,CAAC,eAAe;EAAE,KAAK;EAAK,OAAO;EAAU,QAAQ;EAAgB,OAAO;CAAG,CAAC,GAAG;EAC/G,MAAM;EACN,QAAQ;CACV,CAAC;CACD,YAAY,gBAAgB,CAAC,eAAe;EAAE,KAAK;EAAK,OAAO;EAAQ,QAAQ;EAAe,OAAO;CAAG,CAAC,GAAG;EAC1G,MAAM;EACN,QAAQ;CACV,CAAC;CACD,YAAY,gBAAgB,CAAC,eAAe;EAAE,KAAK;EAAK,OAAO;EAAQ,QAAQ;EAAkB,OAAO;CAAG,CAAC,GAAG;EAC7G,MAAM;EACN,QAAQ;CACV,CAAC;CACD,YAAY,gBAAgB,CAAC,YAAY;EAAE,KAAK;EAAK,OAAO;EAAW,QAAQ;CAAgB,CAAC,GAAG;EACjG,MAAM;EACN,SAAS,EAAE,MAAM,UAAU;CAC7B,CAAC;CACD,YAAY,YAAY,CAAC,YAAY;EAAE,KAAK;EAAK,OAAO;EAAQ,QAAQ;CAAU,CAAC,GAAG;EACpF,MAAM;EACN,QAAQ;CACV,CAAC;AACH;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,WAAW,QAAgB,SAAiB,WAA0C;CAC7F,OAAO;EAAE;EAAQ;EAAS,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;CAAG;AAChE;AAEA,SAAS,yBAAyB,OAAwB;CACxD,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG;EACpD,MAAM,WAAW,MAAM,WAAW,KAAK;EACvC,IAAI,YAAY,kBAAkB,aAAa,kBAAkB,OAAO;CAC1E;CACA,OAAO;AACT;AAEA,SAAS,iBACP,OACA,OACA,eACA,QACA,aACA,WACoB;CACpB,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,GAAG;EAC9C,YAAY,KAAK,WAAW,QAAQ,GAAG,MAAM,+BAA+B,SAAS,CAAC;EACtF;CACF;CACA,MAAM,OAAO,MAAM,KAAK;CACxB,IAAI,KAAK,SAAS,iBAAiB,yBAAyB,IAAI,GAAG;EACjE,YAAY,KACV,WACE,QACA,GAAG,MAAM,mBAAmB,cAAc,iDAC1C,SACF,CACF;EACA;CACF;CACA,OAAO;AACT;AAEA,SAAS,iBACP,OACA,OACA,eACA,QACA,aACA,WACoB;CACpB,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,OAAO,iBAAiB,OAAO,OAAO,eAAe,QAAQ,aAAa,SAAS;AACrF;AAEA,SAAS,aACP,OACA,QACA,WACA,OACA,aAC+B;CAC/B,IAAI,CAAC,SAAS,KAAK,GAAG;EACpB,YAAY,KAAK,WAAW,QAAQ,mBAAmB,MAAM,uBAAuB,SAAS,CAAC;EAC9F;CACF;CAEA,MAAM,MAAM,iBAAiB,MAAM,KAAK,mBAAmB,MAAM,QAAQ,GAAG,QAAQ,aAAa,SAAS;CAC1G,MAAM,QAAQ,iBACZ,MAAM,OACN,mBAAmB,MAAM,UACzB,kBACA,QACA,aACA,SACF;CACA,MAAM,SAAS,iBACb,MAAM,QACN,mBAAmB,MAAM,WACzB,mBACA,QACA,aACA,SACF;CAOA,MAAM,OAAmC,MAAM,SAAS,aAAa,MAAM,SAAS,SAAS,MAAM,OAAO,KAAA;CAC1G,IAAI;CACJ,IAAI,MAAM,UAAU,KAAA,GAAW;EAC7B,IAAI,CAAC,OAAO,UAAU,MAAM,KAAK,KAAM,MAAM,QAAmB,KAAM,MAAM,QAAmB,kBAC7F,YAAY,KACV,WACE,QACA,mBAAmB,MAAM,uCAAuC,iBAAiB,IACjF,SACF,CACF;OAEA,QAAQ,MAAM;CAElB;CACA,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,KAAK,GAAG,GAAG;EACzC,IAAI,OAAO,CAAC,SAAS,KAAK,GAAG,GAC3B,YAAY,KAAK,WAAW,QAAQ,mBAAmB,MAAM,mBAAmB,SAAS,IAAI,SAAS,CAAC;EAEzG;CACF;CACA,OAAO;EACL;EACA;EACA,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAC3B,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;EACvB,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;CACzC;AACF;AAEA,SAAS,aACP,OACA,QACA,WACA,aAC+B;CAC/B,IAAI,CAAC,SAAS,KAAK,GAAG;EACpB,YAAY,KAAK,WAAW,QAAQ,yCAAyC,SAAS,CAAC;EACvF;CACF;CACA,MAAM,OAAO,iBACX,MAAM,MACN,2BACA,yBACA,QACA,aACA,SACF;CACA,MAAM,OAAO,iBACX,MAAM,MACN,2BACA,6BACA,QACA,aACA,SACF;CACA,IAAI,CAAC,QAAQ,CAAC,kBAAkB,KAAK,IAAI,GAAG;EAC1C,IAAI,QAAQ,CAAC,kBAAkB,KAAK,IAAI,GACtC,YAAY,KAAK,WAAW,QAAQ,sCAAsC,kBAAkB,IAAI,SAAS,CAAC;EAE5G;CACF;CACA,OAAO;EAAE;EAAM,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;CAAG;AAC3C;AAEA,SAAS,qBACP,OACA,QACA,WACA,aACuC;CACvC,IAAI,CAAC,SAAS,KAAK,GAAG;EACpB,YAAY,KAAK,WAAW,QAAQ,wCAAwC,SAAS,CAAC;EACtF;CACF;CACA,MAAM,OAAO,iBACX,MAAM,MACN,0BACA,wBACA,QACA,aACA,SACF;CACA,IAAI,CAAC,QAAQ,CAAC,gBAAgB,KAAK,IAAI,GAAG;EACxC,IAAI,QAAQ,CAAC,gBAAgB,KAAK,IAAI,GACpC,YAAY,KAAK,WAAW,QAAQ,qCAAqC,gBAAgB,IAAI,SAAS,CAAC;EAEzG;CACF;CACA,OAAO,EAAE,KAAK;AAChB;AAEA,SAAS,aACP,OACA,QACA,aAC+B;CAC/B,IAAI,CAAC,SAAS,KAAK,GAAG;EACpB,YAAY,KAAK,WAAW,QAAQ,+BAA+B,CAAC;EACpE;CACF;CACA,MAAM,YAAY,iBAAiB,MAAM,IAAI,iBAAiB,uBAAuB,QAAQ,WAAW;CACxG,IAAI,CAAC,WAAW,OAAO,KAAA;CACvB,IAAI,CAAC,gBAAgB,KAAK,SAAS,GAAG;EACpC,YAAY,KAAK,WAAW,QAAQ,4BAA4B,gBAAgB,IAAI,SAAS,CAAC;EAC9F;CACF;CACA,IAAI,CAAC,MAAM,QAAQ,MAAM,IAAI,KAAK,MAAM,KAAK,WAAW,KAAK,MAAM,KAAK,SAAS,iBAAiB;EAChG,YAAY,KACV,WAAW,QAAQ,8CAA8C,gBAAgB,aAAa,SAAS,CACzG;EACA;CACF;CAEA,MAAM,OAAO,MAAM,KAAK,KAAK,SAAS,UAAU,aAAa,SAAS,QAAQ,WAAW,OAAO,WAAW,CAAC;CAC5G,MAAM,aAAa,OAAO,OAAO,OAAO,SAAS;CAEjD,IAAI,eADc,OAAO,OAAO,OAAO,QACZ,GAAG;EAC5B,YAAY,KAAK,WAAW,QAAQ,6DAA6D,SAAS,CAAC;EAC3G;CACF;CACA,MAAM,SAAS,aACX,EAAE,SAAS,aAAa,MAAM,SAAS,QAAQ,WAAW,WAAW,EAAE,IACvE,EAAE,QAAQ,qBAAqB,MAAM,QAAQ,QAAQ,WAAW,WAAW,EAAE;CACjF,IAAI,KAAK,MAAM,YAAY,YAAY,KAAA,CAAS,GAAG,OAAO,KAAA;CAC1D,IAAI,aAAa,QAAQ;EACvB,IAAI,CAAC,OAAO,SAAS,OAAO,KAAA;EAC5B,OAAO;GACL,IAAI;GACE;GACN,SAAS,OAAO;EAClB;CACF;CACA,IAAI,CAAC,OAAO,QAAQ,OAAO,KAAA;CAC3B,OAAO;EACL,IAAI;EACE;EACN,QAAQ,OAAO;CACjB;AACF;AAEA,SAAS,kBAAkB,OAAoC;CAC7D,MAAM,cAAsC,CAAC;CAC7C,IAAI,CAAC,SAAS,KAAK,GACjB,OAAO,EAAE,aAAa,CAAC,WAAW,gBAAgB,wCAAwC,CAAC,EAAE;CAE/F,MAAM,SAAS,iBAAiB,MAAM,QAAQ,UAAU,mBAAmB,gBAAgB,WAAW;CACtG,IAAI,CAAC,QAAQ,OAAO,EAAE,YAAY;CAClC,IAAI,CAAC,YAAY,KAAK,MAAM,GAAG;EAC7B,YAAY,KAAK,WAAW,QAAQ,qBAAqB,YAAY,EAAE,CAAC;EACxE,OAAO,EAAE,YAAY;CACvB;CACA,IAAI,CAAC,MAAM,QAAQ,MAAM,QAAQ,GAC/B,OAAO,EAAE,aAAa,CAAC,WAAW,QAAQ,4BAA4B,CAAC,EAAE;CAG3E,MAAM,WAAW,MAAM,SAAS,KAAK,YAAY,aAAa,SAAS,QAAQ,WAAW,CAAC;CAC3F,MAAM,eAAe,SAClB,SAAS,YAAa,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAE,CAAC,CACnD,QAAQ,IAAI,OAAO,QAAQ,IAAI,QAAQ,EAAE,MAAM,KAAK;CACvD,KAAK,MAAM,MAAM,IAAI,IAAI,YAAY,GACnC,YAAY,KAAK,WAAW,QAAQ,eAAe,GAAG,mBAAmB,EAAE,CAAC;CAE9E,IAAI,YAAY,SAAS,KAAK,SAAS,MAAM,YAAY,YAAY,KAAA,CAAS,GAAG,OAAO,EAAE,YAAY;CAEtG,OAAO;EACL,cAAc;GACZ;GACU;EACZ;EACA;CACF;AACF;AAEA,SAAS,YAAY,SAAoC;CACvD,OAAO,QAAQ,SAAS;AAC1B;AAEA,SAAS,YAAY,QAA4C;CAC/D,IAAI,OAAO,SAAS,WAAW,OAAO;EAAE,MAAM;EAAW,SAAS,EAAE,GAAG,OAAO,QAAQ;CAAE;CACxF,IAAI,OAAO,SAAS,OAAO,OAAO;EAAE,MAAM;EAAO,QAAQ,OAAO;CAAO;CACvE,OAAO;EAAE,MAAM;EAAa,QAAQ,OAAO;EAAQ,QAAQ,EAAE,GAAG,OAAO,OAAO;CAAE;AAClF;AAEA,SAAS,iBAA6B;CACpC,OAAO;EACL,KAAK;EACL,OAAO;EACP,OAAO;EACP,OAAO;EACP,0BAAU,IAAI,IAAI;CACpB;AACF;AAEA,SAAS,UAAU,MAA4C;CAC7D,OAAO,OAAO,KAAK,KAAK,YAAY,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG;AAC3D;;;;;;;;;;;;AAaA,SAAS,gBAAgB,MAAkB,SAAqC;CAC9E,OAAO,KAAK,UAAU,QAAQ,SAAS,KAAK,UAAU,YAAY,OAAO;AAC3E;;AAGA,SAAS,UAAU,MAA0B;CAC3C,IAAI,KAAK,SAAS,SAAS,GAAG,OAAO,KAAK,SAAS,IAAI;CACvD,IAAI,QAAQ;CACZ,KAAK,MAAM,SAAS,KAAK,SAAS,OAAO,GAAG,SAAS,UAAU,KAAK;CACpE,OAAO;AACT;;;;;;;;;;;;;;AAeA,SAAS,cAAc,MAAkB,SAAwD;CAC/F,MAAM,cAAsC,CAAC;CAC7C,IAAI,UAAU;CACd,IAAI,eAAe;CAEnB,KAAK,MAAM,CAAC,OAAO,YAAY,QAAQ,KAAK,QAAQ,GAAG;EACrD,MAAM,WAAW,QAAQ,SAAS,IAAI,QAAQ,GAAG;EACjD,IAAI,CAAC,UAAU;GACb,eAAe;GACf;EACF;EACA,IAAI,CAAC,gBAAgB,UAAU,OAAO,GACpC,YAAY,KACV,WACE,QAAQ,QACR,GAAG,UAAU,QAAQ,KAAK,MAAM,GAAG,QAAQ,CAAC,CAAC,EAAE,wBAAwB,SAAS,MAAM,+BACtF,QAAQ,EACV,CACF;EAKF,IAAI,CAAC,SAAS,UAAU,QAAQ,QAAQ,SAAS,SAAS,QAAQ;EAElE,MAAM,SAAS,UAAU,QAAQ,KAAK,SAAS;EAC/C,MAAM,YAAY,UAAU,QAAQ;EACpC,IAAI,UAAU,YAAY,GAAG;GAC3B,YAAY,KACV,WACE,QAAQ,QACR,GAAG,UAAU,QAAQ,IAAI,EAAE,mBAAmB,SAAS,QAClD,YAAY,IAAI,gBAAgB,UAAU,aAAa,GAAG,IAC/D,QAAQ,EACV,CACF;GACA,SAAS,SAAS,MAAM;GACxB,OAAO,SAAS;GAChB,OAAO,SAAS;EAClB,OAAO,IAAI,CAAC,UAAU,SAAS,QAAQ;GACrC,YAAY,KACV,WACE,QAAQ,QACR,GAAG,UAAU,QAAQ,KAAK,MAAM,GAAG,QAAQ,CAAC,CAAC,EAAE,mBAAmB,SAAS,MAAM,oBACjF,QAAQ,EACV,CACF;GACA,OAAO,SAAS;GAChB,OAAO,SAAS;EAClB;EACA,IAAI,UAAU,SAAS,WAAW,KAAA,GAAW,SAAS,QAAQ,QAAQ;EACtE,UAAU;CACZ;CAEA,MAAM,QAAQ,iBAAiB,KAAK,QAAQ,KAAK,SAAS;CAC1D,KAAK,IAAI,QAAQ,OAAO,QAAQ,QAAQ,KAAK,QAAQ,SAAS,GAAG;EAC/D,MAAM,UAAU,QAAQ,KAAK;EAC7B,MAAM,OAAmB;GACvB,KAAK,QAAQ;GACb,OAAO,QAAQ;GACf,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;GACnD,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;GAC7C,OAAO,YAAY,OAAO;GAC1B,OAAO,QAAQ;GACf,0BAAU,IAAI,IAAI;EACpB;EACA,QAAQ,SAAS,IAAI,QAAQ,KAAK,IAAI;EACtC,UAAU;CACZ;CAEA,QAAQ,SAAS,YAAY,QAAQ,MAAM;CAC3C,QAAQ,YAAY,QAAQ;CAC5B,QAAQ,QAAQ,QAAQ;CACxB,OAAO;AACT;AAEA,IAAa,qBAAb,MAAgC;CAC9B,OAAe,eAAe;CAC9B,gCAAiC,IAAI,IAA0C;CAC/E,4BAA6B,IAAI,IAAgB;CACjD,cAA8C,CAAC;CAC/C,QAAgB;CAEhB,cAAc;EACZ,KAAK,QAAQ;CACf;CAEA,SAAS,OAA8C;EACrD,MAAM,SAAS,KAAK,MAAM,KAAK;EAC/B,IAAI,CAAC,OAAO,UAAU,OAAO;EAC7B,KAAK,QAAQ;EACb,OAAO;GAAE,UAAU;GAAM,aAAa,KAAK,eAAe;EAAE;CAC9D;;CAGA,iBAAiB,OAA8C;EAC7D,OAAO,KAAK,MAAM,KAAK;CACzB;CAEA,MAAc,OAA8C;EAC1D,MAAM,SAAS,kBAAkB,KAAK;EACtC,IAAI,CAAC,OAAO,cAAc,OAAO;GAAE,UAAU;GAAO,aAAa,OAAO;EAAY;EAIpF,KAAK,cAAc,OAAO,OAAO,aAAa,MAAM;EACpD,IAAI,OAAO,aAAa,SAAS,SAAS,GACxC,KAAK,cAAc,IAAI,OAAO,aAAa,QAAQ,OAAO,aAAa,QAAQ;EAEjF,KAAK,QAAQ;EACb,OAAO;GAAE,UAAU;GAAM,aAAa,OAAO;EAAY;CAC3D;;CAGA,QAAgC;EAC9B,IAAI,KAAK,OAAO,KAAK,QAAQ;EAC7B,OAAO,KAAK,YAAY,KAAK,UAAU,EAAE,GAAG,KAAK,EAAE;CACrD;CAEA,SAAS,MAAsD;EAC7D,KAAK,MAAM;EACX,IAAI,UAAU,KAAK;EACnB,KAAK,MAAM,OAAO,MAAM;GACtB,MAAM,OAAO,QAAQ,SAAS,IAAI,GAAG;GACrC,IAAI,CAAC,QAAQ,KAAK,QAAQ,OAAO,KAAA;GACjC,UAAU;EACZ;EAOA,MAAM,UAAU,CAAC,GAAG,QAAQ,SAAS,OAAO,CAAC,CAAC,CAC3C,MAAM,MAAM,UAAU,KAAK,IAAI,cAAc,MAAM,GAAG,CAAC,CAAC,CACxD,KAAK,UAAoC;GACxC,KAAK,KAAK;GACV,OAAO,KAAK;GACZ,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;GAC7C,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;GACvC,aAAa,KAAK,SAAS,OAAO;GAClC,GAAI,KAAK,SAAS,EAAE,QAAQ,YAAY,KAAK,MAAM,EAAE,IAAI,CAAC;EAC5D,EAAE;EACJ,OAAO;GAAE,OAAO,QAAQ;GAAO;EAAQ;CACzC;CAEA,iBAAyC;EACvC,OAAO,KAAK,MAAM;CACpB;CAEA,UAAU,UAAkC;EAC1C,KAAK,UAAU,IAAI,QAAQ;EAC3B,aAAa,KAAK,UAAU,OAAO,QAAQ;CAC7C;CAEA,UAAwB;EACtB,MAAM,WAAW,eAAe;EAChC,MAAM,cAAsC,CAAC;EAC7C,KAAK,MAAM,WAAW,eAAe;GAGnC,MAAM,CAAC,SAAS,cAAc,UAAU,OAAO;GAC/C,IAAI,OAAO,MAAM,IAAI,MAAM,gCAAgC,MAAM,SAAS;EAC5E;EAMA,KAAK,MAAM,CAAC,QAAQ,aAAa,KAAK,cAAc,QAAQ,GAC1D,KAAK,MAAM,WAAW,CAAC,GAAG,QAAQ,CAAC,CAAC,MAAM,MAAM,UAAU,KAAK,GAAG,cAAc,MAAM,EAAE,CAAC,GACvF,YAAY,KACV,GAAG,cAAc,UAAU;GACzB,IAAI,QAAQ;GACZ;GACA,MAAM,QAAQ;GACd,QACE,aAAa,UACT;IAAE,MAAM;IAAW,SAAS,QAAQ;GAAQ,IAC5C;IAAE,MAAM;IAAa;IAAQ,QAAQ,QAAQ;GAAO;EAC5D,CAAC,CACH;EAIJ,KAAK,OAAO;EACZ,KAAK,cAAc;EACnB,KAAK,QAAQ;EACb,KAAK,MAAM,YAAY,KAAK,WAAW,SAAS;CAClD;AACF"}
|
|
1
|
+
{"version":3,"file":"leaderRegistry.mjs","names":[],"sources":["../src/services/leaderRegistry/index.ts"],"sourcesContent":["type AppKeybinding =\n | 'app.model.select'\n | 'app.model.cycleForward'\n | 'app.thinking.cycle'\n | 'app.session.new'\n | 'app.session.resume'\n | 'app.session.tree'\n | 'app.session.fork'\n | 'app.editor.external'\n | 'app.exit';\n\nimport type {\n LeaderBinding as PoLeaderBinding,\n LeaderCommand as PoLeaderCommand,\n LeaderContribution as PoLeaderContribution,\n LeaderAction as PoLeaderExtensionAction,\n LeaderSegment as PoLeaderSegment,\n LeaderTone as PoLeaderTone,\n} from '@agimon-ai/po-core/leader';\n\ninterface AppLeaderAction {\n type: 'app';\n action: AppKeybinding;\n}\n\ninterface CommandLeaderAction {\n type: 'command';\n command: PoLeaderCommand;\n}\n\ninterface ExtensionLeaderAction {\n type: 'extension';\n source: string;\n action: PoLeaderExtensionAction;\n}\n\nexport type PoLeaderAction = AppLeaderAction | CommandLeaderAction | ExtensionLeaderAction;\n\nexport interface PoLeaderResolvedOption {\n key: string;\n label: string;\n detail?: string;\n tone?: PoLeaderTone;\n hasChildren: boolean;\n action?: PoLeaderAction;\n}\n\nexport interface PoLeaderGroup {\n label: string;\n options: readonly PoLeaderResolvedOption[];\n}\n\nexport interface PoLeaderDiagnostic {\n source: string;\n bindingId?: string;\n message: string;\n}\n\nexport interface PoLeaderRegistrationResult {\n accepted: boolean;\n diagnostics: readonly PoLeaderDiagnostic[];\n}\n\ninterface InternalLeaderBinding {\n id: string;\n source: string;\n path: readonly [PoLeaderSegment, ...PoLeaderSegment[]];\n action: PoLeaderAction;\n}\n\ninterface LeaderNode {\n key: string;\n label: string;\n detail?: string;\n tone?: PoLeaderTone;\n order: number;\n owner: string;\n bindingId?: string;\n children: Map<string, LeaderNode>;\n action?: PoLeaderAction;\n}\n\ninterface ParsedContribution {\n contribution?: PoLeaderContribution;\n diagnostics: PoLeaderDiagnostic[];\n}\n\nconst CORE_SOURCE = '@agimon-ai/po-ui';\nconst UNKNOWN_SOURCE = 'unknown';\nconst ROOT_LABEL = 'leader';\nconst DEFAULT_OPTION_ORDER = 500;\nconst MAX_OPTION_ORDER = 1000;\nconst MAX_PATH_LENGTH = 4;\nconst MAX_SOURCE_LENGTH = 128;\nconst MAX_BINDING_ID_LENGTH = 128;\nconst MAX_LABEL_LENGTH = 32;\nconst MAX_DETAIL_LENGTH = 80;\nconst MAX_COMMAND_NAME_LENGTH = 64;\nconst MAX_COMMAND_ARGUMENT_LENGTH = 256;\nconst MAX_ACTION_NAME_LENGTH = 128;\nconst SAFE_KEY = /^[a-z0-9]$/;\nconst SAFE_IDENTIFIER = /^[a-z0-9][a-z0-9._:-]*$/;\nconst SAFE_SOURCE = /^[A-Za-z0-9@][A-Za-z0-9@/._:-]*$/;\nconst SAFE_COMMAND_NAME = /^[a-z0-9][a-z0-9:_-]*$/;\nconst C0_CONTROL_MAX = 31;\nconst DELETE_CHARACTER = 127;\nconst MODEL_GROUP = { key: 'm', label: 'models', detail: 'model and thinking level', order: 10 } as const;\nconst SESSION_GROUP = { key: 's', label: 'sessions', detail: 'history, forks and branches', order: 30 } as const;\nconst EXTENSION_GROUP = { key: 'e', label: 'extension', detail: 'tools, skills and config', order: 50 } as const;\nconst HELP_GROUP = { key: 'h', label: 'help', detail: 'package docs and logs', order: 70 } as const;\nconst QUIT_GROUP = { key: 'q', label: 'quit', detail: 'leave the session', order: 90 } as const;\n\nfunction coreBinding(\n id: string,\n path: readonly [PoLeaderSegment, ...PoLeaderSegment[]],\n action: PoLeaderAction,\n): InternalLeaderBinding {\n return { id, source: CORE_SOURCE, path, action };\n}\n\nconst CORE_BINDINGS: readonly InternalLeaderBinding[] = [\n coreBinding('model.select', [MODEL_GROUP, { key: 'm', label: 'select', detail: 'choose model', order: 10 }], {\n type: 'app',\n action: 'app.model.select',\n }),\n coreBinding('model.next', [MODEL_GROUP, { key: 'n', label: 'next', detail: 'cycle model', order: 20 }], {\n type: 'app',\n action: 'app.model.cycleForward',\n }),\n coreBinding('thinking.cycle', [MODEL_GROUP, { key: 't', label: 'thinking', detail: 'cycle level', order: 30 }], {\n type: 'app',\n action: 'app.thinking.cycle',\n }),\n coreBinding('tools.open', [EXTENSION_GROUP, { key: 't', label: 'tools', detail: 'browse tools', order: 20 }], {\n type: 'command',\n command: { name: 'tools' },\n }),\n // Core rather than contributed, so no extension owns the key and the panel is\n // reachable even when nothing has contributed a section to it yet.\n coreBinding('config.open', [EXTENSION_GROUP, { key: 'c', label: 'config', detail: 'settings', order: 30 }], {\n type: 'command',\n command: { name: 'config' },\n }),\n coreBinding('session.new', [SESSION_GROUP, { key: 'n', label: 'new', detail: 'fresh session', order: 10 }], {\n type: 'app',\n action: 'app.session.new',\n }),\n coreBinding('session.resume', [SESSION_GROUP, { key: 'r', label: 'resume', detail: 'open history', order: 20 }], {\n type: 'app',\n action: 'app.session.resume',\n }),\n coreBinding('session.tree', [SESSION_GROUP, { key: 't', label: 'tree', detail: 'branch view', order: 30 }], {\n type: 'app',\n action: 'app.session.tree',\n }),\n coreBinding('session.fork', [SESSION_GROUP, { key: 'f', label: 'fork', detail: 'from selection', order: 40 }], {\n type: 'app',\n action: 'app.session.fork',\n }),\n coreBinding('help.hotkeys', [HELP_GROUP, { key: 'h', label: 'hotkeys', detail: 'show bindings' }], {\n type: 'command',\n command: { name: 'hotkeys' },\n }),\n coreBinding('app.exit', [QUIT_GROUP, { key: 'q', label: 'quit', detail: 'exit Pi' }], {\n type: 'app',\n action: 'app.exit',\n }),\n];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction diagnostic(source: string, message: string, bindingId?: string): PoLeaderDiagnostic {\n return { source, message, ...(bindingId ? { bindingId } : {}) };\n}\n\nfunction containsControlCharacter(value: string): boolean {\n for (let index = 0; index < value.length; index += 1) {\n const codeUnit = value.charCodeAt(index);\n if (codeUnit <= C0_CONTROL_MAX || codeUnit === DELETE_CHARACTER) return true;\n }\n return false;\n}\n\nfunction readRequiredText(\n value: unknown,\n field: string,\n maximumLength: number,\n source: string,\n diagnostics: PoLeaderDiagnostic[],\n bindingId?: string,\n): string | undefined {\n if (typeof value !== 'string' || !value.trim()) {\n diagnostics.push(diagnostic(source, `${field} must be a non-empty string.`, bindingId));\n return undefined;\n }\n const text = value.trim();\n if (text.length > maximumLength || containsControlCharacter(text)) {\n diagnostics.push(\n diagnostic(\n source,\n `${field} must be at most ${maximumLength} characters and contain no control characters.`,\n bindingId,\n ),\n );\n return undefined;\n }\n return text;\n}\n\nfunction readOptionalText(\n value: unknown,\n field: string,\n maximumLength: number,\n source: string,\n diagnostics: PoLeaderDiagnostic[],\n bindingId: string,\n): string | undefined {\n if (value === undefined) return undefined;\n return readRequiredText(value, field, maximumLength, source, diagnostics, bindingId);\n}\n\nfunction parseSegment(\n value: unknown,\n source: string,\n bindingId: string,\n index: number,\n diagnostics: PoLeaderDiagnostic[],\n): PoLeaderSegment | undefined {\n if (!isRecord(value)) {\n diagnostics.push(diagnostic(source, `bindings[].path[${index}] must be an object.`, bindingId));\n return undefined;\n }\n\n const key = readRequiredText(value.key, `bindings[].path[${index}].key`, 1, source, diagnostics, bindingId);\n const label = readRequiredText(\n value.label,\n `bindings[].path[${index}].label`,\n MAX_LABEL_LENGTH,\n source,\n diagnostics,\n bindingId,\n );\n const detail = readOptionalText(\n value.detail,\n `bindings[].path[${index}].detail`,\n MAX_DETAIL_LENGTH,\n source,\n diagnostics,\n bindingId,\n );\n // An unrecognised tone is ignored, not diagnosed. Every other malformed field\n // here rejects the whole contribution, which is right for a key or a label but\n // not for a badge colour: it would mean a package compiled against a later\n // contract loses its entire menu on an older host over a cosmetic value. The\n // union type already catches a typo at every call site built against this\n // contract, which is the only place a typo can come from.\n const tone: PoLeaderTone | undefined = value.tone === 'default' || value.tone === 'exit' ? value.tone : undefined;\n let order: number | undefined;\n if (value.order !== undefined) {\n if (!Number.isInteger(value.order) || (value.order as number) < 0 || (value.order as number) > MAX_OPTION_ORDER) {\n diagnostics.push(\n diagnostic(\n source,\n `bindings[].path[${index}].order must be an integer from 0 to ${MAX_OPTION_ORDER}.`,\n bindingId,\n ),\n );\n } else {\n order = value.order as number;\n }\n }\n if (!key || !label || !SAFE_KEY.test(key)) {\n if (key && !SAFE_KEY.test(key)) {\n diagnostics.push(diagnostic(source, `bindings[].path[${index}].key must match ${SAFE_KEY}.`, bindingId));\n }\n return undefined;\n }\n return {\n key,\n label,\n ...(detail ? { detail } : {}),\n ...(tone ? { tone } : {}),\n ...(order === undefined ? {} : { order }),\n };\n}\n\nfunction parseCommand(\n value: unknown,\n source: string,\n bindingId: string,\n diagnostics: PoLeaderDiagnostic[],\n): PoLeaderCommand | undefined {\n if (!isRecord(value)) {\n diagnostics.push(diagnostic(source, 'bindings[].command must be an object.', bindingId));\n return undefined;\n }\n const name = readRequiredText(\n value.name,\n 'bindings[].command.name',\n MAX_COMMAND_NAME_LENGTH,\n source,\n diagnostics,\n bindingId,\n );\n const args = readOptionalText(\n value.args,\n 'bindings[].command.args',\n MAX_COMMAND_ARGUMENT_LENGTH,\n source,\n diagnostics,\n bindingId,\n );\n if (!name || !SAFE_COMMAND_NAME.test(name)) {\n if (name && !SAFE_COMMAND_NAME.test(name)) {\n diagnostics.push(diagnostic(source, `bindings[].command.name must match ${SAFE_COMMAND_NAME}.`, bindingId));\n }\n return undefined;\n }\n return { name, ...(args ? { args } : {}) };\n}\n\nfunction parseExtensionAction(\n value: unknown,\n source: string,\n bindingId: string,\n diagnostics: PoLeaderDiagnostic[],\n): PoLeaderExtensionAction | undefined {\n if (!isRecord(value)) {\n diagnostics.push(diagnostic(source, 'bindings[].action must be an object.', bindingId));\n return undefined;\n }\n const name = readRequiredText(\n value.name,\n 'bindings[].action.name',\n MAX_ACTION_NAME_LENGTH,\n source,\n diagnostics,\n bindingId,\n );\n if (!name || !SAFE_IDENTIFIER.test(name)) {\n if (name && !SAFE_IDENTIFIER.test(name)) {\n diagnostics.push(diagnostic(source, `bindings[].action.name must match ${SAFE_IDENTIFIER}.`, bindingId));\n }\n return undefined;\n }\n return { name };\n}\n\nfunction parseBinding(value: unknown, source: string, diagnostics: PoLeaderDiagnostic[]): PoLeaderBinding | undefined {\n if (!isRecord(value)) {\n diagnostics.push(diagnostic(source, 'bindings[] must be an object.'));\n return undefined;\n }\n const bindingId = readRequiredText(value.id, 'bindings[].id', MAX_BINDING_ID_LENGTH, source, diagnostics);\n if (!bindingId) return undefined;\n if (!SAFE_IDENTIFIER.test(bindingId)) {\n diagnostics.push(diagnostic(source, `bindings[].id must match ${SAFE_IDENTIFIER}.`, bindingId));\n return undefined;\n }\n if (!Array.isArray(value.path) || value.path.length === 0 || value.path.length > MAX_PATH_LENGTH) {\n diagnostics.push(\n diagnostic(source, `bindings[].path must contain between 1 and ${MAX_PATH_LENGTH} segments.`, bindingId),\n );\n return undefined;\n }\n\n const path = value.path.map((segment, index) => parseSegment(segment, source, bindingId, index, diagnostics));\n const hasCommand = Object.hasOwn(value, 'command');\n const hasAction = Object.hasOwn(value, 'action');\n if (hasCommand === hasAction) {\n diagnostics.push(diagnostic(source, 'bindings[] must contain exactly one of command or action.', bindingId));\n return undefined;\n }\n const target = hasCommand\n ? { command: parseCommand(value.command, source, bindingId, diagnostics) }\n : { action: parseExtensionAction(value.action, source, bindingId, diagnostics) };\n if (path.some((segment) => segment === undefined)) return undefined;\n if ('command' in target) {\n if (!target.command) return undefined;\n return {\n id: bindingId,\n path: path as [PoLeaderSegment, ...PoLeaderSegment[]],\n command: target.command,\n };\n }\n if (!target.action) return undefined;\n return {\n id: bindingId,\n path: path as [PoLeaderSegment, ...PoLeaderSegment[]],\n action: target.action,\n };\n}\n\nfunction parseContribution(value: unknown): ParsedContribution {\n const diagnostics: PoLeaderDiagnostic[] = [];\n if (!isRecord(value)) {\n return { diagnostics: [diagnostic(UNKNOWN_SOURCE, 'Leader contribution must be an object.')] };\n }\n const source = readRequiredText(value.source, 'source', MAX_SOURCE_LENGTH, UNKNOWN_SOURCE, diagnostics);\n if (!source) return { diagnostics };\n if (!SAFE_SOURCE.test(source)) {\n diagnostics.push(diagnostic(source, `source must match ${SAFE_SOURCE}.`));\n return { diagnostics };\n }\n if (!Array.isArray(value.bindings)) {\n return { diagnostics: [diagnostic(source, 'bindings must be an array.')] };\n }\n\n const bindings = value.bindings.map((binding) => parseBinding(binding, source, diagnostics));\n const duplicateIds = bindings\n .flatMap((binding) => (binding ? [binding.id] : []))\n .filter((id, index, ids) => ids.indexOf(id) !== index);\n for (const id of new Set(duplicateIds)) {\n diagnostics.push(diagnostic(source, `Binding id '${id}' is duplicated.`, id));\n }\n if (diagnostics.length > 0 || bindings.some((binding) => binding === undefined)) return { diagnostics };\n\n return {\n contribution: {\n source,\n bindings: bindings as PoLeaderBinding[],\n },\n diagnostics,\n };\n}\n\nfunction optionOrder(segment: PoLeaderSegment): number {\n return segment.order ?? DEFAULT_OPTION_ORDER;\n}\n\nfunction cloneAction(action: PoLeaderAction): PoLeaderAction {\n if (action.type === 'command') return { type: 'command', command: { ...action.command } };\n if (action.type === 'app') return { type: 'app', action: action.action };\n return { type: 'extension', source: action.source, action: { ...action.action } };\n}\n\nfunction createRootNode(): LeaderNode {\n return {\n key: '',\n label: ROOT_LABEL,\n order: 0,\n owner: CORE_SOURCE,\n children: new Map(),\n };\n}\n\nfunction pathLabel(path: readonly PoLeaderSegment[]): string {\n return `SPC ${path.map((segment) => segment.key).join(' ')}`;\n}\n\n/**\n * Whether a shared group prefix is worded the same way by both contributors.\n *\n * `detail` is left out on purpose: it is a subtitle, and these packages ship on\n * independent npm versions, so comparing it means one upgraded package disagrees\n * with every package that has not caught up.\n *\n * A mismatch is REPORTED, never fatal. Losing `SPC e s` because two packages\n * spell the `SPC e` group differently costs the user a key they can no longer\n * press, which is worse than a menu row worded by whoever registered first.\n */\nfunction metadataMatches(node: LeaderNode, segment: PoLeaderSegment): boolean {\n return node.label === segment.label && node.order === optionOrder(segment);\n}\n\n/** Every binding under a node, for a takeover message that says what it displaced. */\nfunction leafCount(node: LeaderNode): number {\n if (node.children.size === 0) return node.action ? 1 : 0;\n let total = 0;\n for (const child of node.children.values()) total += leafCount(child);\n return total;\n}\n\n/**\n * Inserts one binding, taking a key over from whoever holds it.\n *\n * LAST REGISTRATION WINS, and the diagnostics say who lost what. The rule this\n * replaced rejected the newcomer, which read as \"warning plus nothing happened\":\n * the key stayed bound to the first registrant, or - when the clash was only a\n * group label - to nobody at all. A takeover is recoverable in a way a dropped\n * binding is not: the tree is rebuilt from every stored contribution, so when\n * the winner unregisters, the displaced binding comes back on the next rebuild.\n *\n * Returns diagnostics rather than one issue, because a single insert can both\n * disagree about a group's wording and take a key from someone.\n */\nfunction insertBinding(root: LeaderNode, binding: InternalLeaderBinding): PoLeaderDiagnostic[] {\n const diagnostics: PoLeaderDiagnostic[] = [];\n let current = root;\n let firstMissing = -1;\n\n for (const [index, segment] of binding.path.entries()) {\n const existing = current.children.get(segment.key);\n if (!existing) {\n firstMissing = index;\n break;\n }\n if (!metadataMatches(existing, segment)) {\n diagnostics.push(\n diagnostic(\n binding.source,\n `${pathLabel(binding.path.slice(0, index + 1))} keeps the label from ${existing.owner}; the binding is registered.`,\n binding.id,\n ),\n );\n }\n // First writer wins for the subtitle, but a group created without one still\n // adopts the first that is offered, so the wording does not depend on which\n // package happened to register first.\n if (!existing.detail && segment.detail) existing.detail = segment.detail;\n\n const isLeaf = index === binding.path.length - 1;\n const displaced = leafCount(existing);\n if (isLeaf && displaced > 0) {\n diagnostics.push(\n diagnostic(\n binding.source,\n `${pathLabel(binding.path)} taken over from ${existing.owner}` +\n `${displaced > 1 ? `, displacing ${displaced} bindings` : ''}.`,\n binding.id,\n ),\n );\n existing.children.clear();\n delete existing.action;\n delete existing.bindingId;\n } else if (!isLeaf && existing.action) {\n diagnostics.push(\n diagnostic(\n binding.source,\n `${pathLabel(binding.path.slice(0, index + 1))} taken over from ${existing.owner} to open a group.`,\n binding.id,\n ),\n );\n delete existing.action;\n delete existing.bindingId;\n }\n if (isLeaf || existing.action === undefined) existing.owner = binding.source;\n current = existing;\n }\n\n const start = firstMissing === -1 ? binding.path.length : firstMissing;\n for (let index = start; index < binding.path.length; index += 1) {\n const segment = binding.path[index] as PoLeaderSegment;\n const node: LeaderNode = {\n key: segment.key,\n label: segment.label,\n ...(segment.detail ? { detail: segment.detail } : {}),\n ...(segment.tone ? { tone: segment.tone } : {}),\n order: optionOrder(segment),\n owner: binding.source,\n children: new Map(),\n };\n current.children.set(segment.key, node);\n current = node;\n }\n\n current.action = cloneAction(binding.action);\n current.bindingId = binding.id;\n current.owner = binding.source;\n return diagnostics;\n}\n\nexport class PoLeaderRegistry {\n private root = createRootNode();\n private readonly contributions = new Map<string, readonly PoLeaderBinding[]>();\n private readonly listeners = new Set<() => void>();\n private diagnostics: PoLeaderDiagnostic[] = [];\n private dirty = false;\n\n constructor() {\n this.rebuild();\n }\n\n register(value: unknown): PoLeaderRegistrationResult {\n const result = this.apply(value);\n if (!result.accepted) return result;\n this.rebuild();\n return { accepted: true, diagnostics: this.getDiagnostics() };\n }\n\n /** Records a contribution without rebuilding the whole tree immediately. */\n registerDeferred(value: unknown): PoLeaderRegistrationResult {\n return this.apply(value);\n }\n\n private apply(value: unknown): PoLeaderRegistrationResult {\n const parsed = parseContribution(value);\n if (!parsed.contribution) return { accepted: false, diagnostics: parsed.diagnostics };\n\n // Deleted before it is set again, so re-registering moves a source to the\n // back of the insertion order: registering again IS claiming again.\n this.contributions.delete(parsed.contribution.source);\n if (parsed.contribution.bindings.length > 0) {\n this.contributions.set(parsed.contribution.source, parsed.contribution.bindings);\n }\n this.dirty = true;\n return { accepted: true, diagnostics: parsed.diagnostics };\n }\n\n /** Rebuilds one batch and returns its deterministic conflict diagnostics. */\n flush(): PoLeaderDiagnostic[] {\n if (this.dirty) this.rebuild();\n return this.diagnostics.map((item) => ({ ...item }));\n }\n\n getGroup(path: readonly string[]): PoLeaderGroup | undefined {\n this.flush();\n let current = this.root;\n for (const key of path) {\n const next = current.children.get(key);\n if (!next || next.action) return undefined;\n current = next;\n }\n\n // The key is the sort, at every level. A reader looking for a chord knows the\n // key, not the number some contributor picked for it, and those numbers live\n // across nine packages so no one of them can see the sequence they produce.\n // `order` survives only as group identity: co-contributors to a shared prefix\n // must still agree on it, which is what `metadataMatches` checks.\n const options = [...current.children.values()]\n .sort((left, right) => left.key.localeCompare(right.key))\n .map((node): PoLeaderResolvedOption => ({\n key: node.key,\n label: node.label,\n ...(node.detail ? { detail: node.detail } : {}),\n ...(node.tone ? { tone: node.tone } : {}),\n hasChildren: node.children.size > 0,\n ...(node.action ? { action: cloneAction(node.action) } : {}),\n }));\n return { label: current.label, options };\n }\n\n getDiagnostics(): PoLeaderDiagnostic[] {\n return this.flush();\n }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private rebuild(): void {\n const nextRoot = createRootNode();\n const diagnostics: PoLeaderDiagnostic[] = [];\n for (const binding of CORE_BINDINGS) {\n // Core bindings land in an empty tree, so anything reported here is two\n // core bindings disagreeing with each other: a defect, not a contribution.\n const [issue] = insertBinding(nextRoot, binding);\n if (issue) throw new Error(`Invalid core leader binding: ${issue.message}`);\n }\n\n // Registration order, not alphabetical: the key belongs to whoever claimed\n // it last, and `apply` re-seats a source that registers again so a reload\n // counts as a fresh claim. Bindings inside one contribution stay sorted by\n // id, so a single source's own output is still deterministic.\n for (const [source, bindings] of this.contributions.entries()) {\n for (const binding of [...bindings].sort((left, right) => left.id.localeCompare(right.id))) {\n diagnostics.push(\n ...insertBinding(nextRoot, {\n id: binding.id,\n source,\n path: binding.path,\n action:\n 'command' in binding\n ? { type: 'command', command: binding.command }\n : { type: 'extension', source, action: binding.action },\n }),\n );\n }\n }\n\n this.root = nextRoot;\n this.diagnostics = diagnostics;\n this.dirty = false;\n for (const listener of this.listeners) listener();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_telemetry = require("./telemetry-
|
|
2
|
+
const require_telemetry = require("./telemetry-EXlQc9s0.cjs");
|
|
3
3
|
exports.UI_EVENT = require_telemetry.UI_EVENT;
|
|
4
4
|
exports.createUiTelemetry = require_telemetry.createUiTelemetry;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as UiTelemetryOptions, i as UiTelemetry, n as UiEventAttributes, o as createUiTelemetry, r as UiEventName, t as UI_EVENT } from "./index-
|
|
1
|
+
import { a as UiTelemetryOptions, i as UiTelemetry, n as UiEventAttributes, o as createUiTelemetry, r as UiEventName, t as UI_EVENT } from "./index-2GW2dj0I.mjs";
|
|
2
2
|
export { UI_EVENT, type UiEventAttributes, type UiEventName, type UiTelemetry, type UiTelemetryOptions, createUiTelemetry };
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { UI_EVENT, createUiTelemetry };
|
|
1
|
+
export { t as UI_EVENT, n as createUiTelemetry } from "./telemetry-RiFqd0cg.mjs";
|
package/dist/skills.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
let _agimon_ai_po_core_skills = require("@agimon-ai/po-core/skills");
|
|
3
|
-
Object.defineProperty(exports, "
|
|
3
|
+
Object.defineProperty(exports, "PO_SKILL_SOURCES_SERVICE", {
|
|
4
4
|
enumerable: true,
|
|
5
5
|
get: function() {
|
|
6
|
-
return _agimon_ai_po_core_skills.
|
|
6
|
+
return _agimon_ai_po_core_skills.PO_SKILL_SOURCES_SERVICE;
|
|
7
7
|
}
|
|
8
8
|
});
|
|
9
9
|
Object.defineProperty(exports, "SkillSourceContributionSchema", {
|
|
@@ -18,21 +18,21 @@ Object.defineProperty(exports, "SkillSourceNameSchema", {
|
|
|
18
18
|
return _agimon_ai_po_core_skills.SkillSourceNameSchema;
|
|
19
19
|
}
|
|
20
20
|
});
|
|
21
|
-
Object.defineProperty(exports, "
|
|
21
|
+
Object.defineProperty(exports, "createPoSkillSourcesService", {
|
|
22
22
|
enumerable: true,
|
|
23
23
|
get: function() {
|
|
24
|
-
return _agimon_ai_po_core_skills.
|
|
24
|
+
return _agimon_ai_po_core_skills.createPoSkillSourcesService;
|
|
25
25
|
}
|
|
26
26
|
});
|
|
27
|
-
Object.defineProperty(exports, "
|
|
27
|
+
Object.defineProperty(exports, "readPoSkillSourcesService", {
|
|
28
28
|
enumerable: true,
|
|
29
29
|
get: function() {
|
|
30
|
-
return _agimon_ai_po_core_skills.
|
|
30
|
+
return _agimon_ai_po_core_skills.readPoSkillSourcesService;
|
|
31
31
|
}
|
|
32
32
|
});
|
|
33
|
-
Object.defineProperty(exports, "
|
|
33
|
+
Object.defineProperty(exports, "requirePoSkillSourcesService", {
|
|
34
34
|
enumerable: true,
|
|
35
35
|
get: function() {
|
|
36
|
-
return _agimon_ai_po_core_skills.
|
|
36
|
+
return _agimon_ai_po_core_skills.requirePoSkillSourcesService;
|
|
37
37
|
}
|
|
38
38
|
});
|
package/dist/skills.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { PO_SKILL_SOURCES_SERVICE, PoSkillSourcesService, SkillSourceContribution, SkillSourceContributionHandle, SkillSourceContributionSchema, SkillSourceNameSchema, createPoSkillSourcesService, readPoSkillSourcesService, requirePoSkillSourcesService } from "@agimon-ai/po-core/skills";
|
|
2
|
+
export { PO_SKILL_SOURCES_SERVICE, type PoSkillSourcesService, type SkillSourceContribution, type SkillSourceContributionHandle, SkillSourceContributionSchema, SkillSourceNameSchema, createPoSkillSourcesService, readPoSkillSourcesService, requirePoSkillSourcesService };
|
package/dist/skills.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { DOOM_SKILL_SOURCES_SERVICE, SkillSourceContributionSchema, SkillSourceNameSchema, createDoomSkillSourcesService, readDoomSkillSourcesService, requireDoomSkillSourcesService };
|
|
1
|
+
export { PO_SKILL_SOURCES_SERVICE, SkillSourceContributionSchema, SkillSourceNameSchema, createPoSkillSourcesService, readPoSkillSourcesService, requirePoSkillSourcesService } from "@agimon-ai/po-core/skills";
|
|
@@ -3,15 +3,15 @@ let _agimon_ai_po_telemetry = require("@agimon-ai/po-telemetry");
|
|
|
3
3
|
const SERVICE_NAME = "po-ui";
|
|
4
4
|
const PACKAGE_NAME = "@agimon-ai/po-ui";
|
|
5
5
|
const UI_EVENT = {
|
|
6
|
-
leaderContributionRejected: "
|
|
7
|
-
leaderCommandUnavailable: "
|
|
8
|
-
leaderActionUnavailable: "
|
|
9
|
-
themeApplyFailed: "
|
|
10
|
-
shellInstalled: "
|
|
11
|
-
eventLoopStalled: "
|
|
6
|
+
leaderContributionRejected: "po_pi_ui.leader_contribution_rejected",
|
|
7
|
+
leaderCommandUnavailable: "po_pi_ui.leader_command_unavailable",
|
|
8
|
+
leaderActionUnavailable: "po_pi_ui.leader_action_unavailable",
|
|
9
|
+
themeApplyFailed: "po_pi_ui.theme_apply_failed",
|
|
10
|
+
shellInstalled: "po_pi_ui.shell_installed",
|
|
11
|
+
eventLoopStalled: "po_pi_ui.event_loop_stalled"
|
|
12
12
|
};
|
|
13
13
|
function createUiTelemetry(options = {}) {
|
|
14
|
-
const telemetry = (0, _agimon_ai_po_telemetry.
|
|
14
|
+
const telemetry = (0, _agimon_ai_po_telemetry.createPoTelemetry)({
|
|
15
15
|
serviceName: SERVICE_NAME,
|
|
16
16
|
packageName: PACKAGE_NAME,
|
|
17
17
|
cwd: options.cwd,
|
|
@@ -44,4 +44,4 @@ Object.defineProperty(exports, "createUiTelemetry", {
|
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
//# sourceMappingURL=telemetry-
|
|
47
|
+
//# sourceMappingURL=telemetry-EXlQc9s0.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry-EXlQc9s0.cjs","names":["createPoTelemetry"],"sources":["../src/services/telemetry/index.ts"],"sourcesContent":["import { createPoTelemetry, type PoTelemetry, type PoTelemetryOptions } from '@agimon-ai/po-telemetry';\n\nconst SERVICE_NAME = 'po-ui';\nconst PACKAGE_NAME = '@agimon-ai/po-ui';\n\nexport const UI_EVENT = {\n leaderContributionRejected: 'po_pi_ui.leader_contribution_rejected',\n leaderCommandUnavailable: 'po_pi_ui.leader_command_unavailable',\n leaderActionUnavailable: 'po_pi_ui.leader_action_unavailable',\n themeApplyFailed: 'po_pi_ui.theme_apply_failed',\n shellInstalled: 'po_pi_ui.shell_installed',\n eventLoopStalled: 'po_pi_ui.event_loop_stalled',\n} as const;\n\nexport type UiEventName = (typeof UI_EVENT)[keyof typeof UI_EVENT];\nexport type UiEventAttributes = Record<string, string | number | boolean>;\n\nexport interface UiTelemetryOptions {\n cwd?: string;\n workspaceRoot?: string;\n env?: NodeJS.ProcessEnv;\n telemetryFactory?: NonNullable<PoTelemetryOptions['telemetryFactory']>;\n warn?: (message: string) => void;\n}\n\nexport interface UiTelemetry {\n recordError(event: UiEventName, error: unknown, attributes?: UiEventAttributes): Promise<void>;\n recordWarning(event: UiEventName, error: unknown, attributes?: UiEventAttributes): Promise<void>;\n recordEvent(event: UiEventName, attributes?: UiEventAttributes): Promise<void>;\n flush(): Promise<void>;\n shutdown(): Promise<void>;\n}\n\nexport function createUiTelemetry(options: UiTelemetryOptions = {}): UiTelemetry {\n const telemetry: PoTelemetry = createPoTelemetry({\n serviceName: SERVICE_NAME,\n packageName: PACKAGE_NAME,\n cwd: options.cwd,\n workspaceRoot: options.workspaceRoot,\n env: options.env,\n telemetryFactory: options.telemetryFactory,\n warn: options.warn,\n enableLogs: true,\n enableTraces: true,\n });\n return {\n recordError: (event, error, attributes) => telemetry.recordError(event, error, attributes),\n recordWarning: (event, error, attributes) => telemetry.recordWarning(event, error, attributes),\n recordEvent: (event, attributes) => telemetry.recordEvent(event, attributes),\n flush: () => telemetry.flush(),\n shutdown: () => telemetry.shutdown(),\n };\n}\n"],"mappings":";;AAEA,MAAM,eAAe;AACrB,MAAM,eAAe;AAErB,MAAa,WAAW;CACtB,4BAA4B;CAC5B,0BAA0B;CAC1B,yBAAyB;CACzB,kBAAkB;CAClB,gBAAgB;CAChB,kBAAkB;AACpB;AAqBA,SAAgB,kBAAkB,UAA8B,CAAC,GAAgB;CAC/E,MAAM,aAAA,GAAyBA,wBAAAA,kBAAAA,CAAkB;EAC/C,aAAa;EACb,aAAa;EACb,KAAK,QAAQ;EACb,eAAe,QAAQ;EACvB,KAAK,QAAQ;EACb,kBAAkB,QAAQ;EAC1B,MAAM,QAAQ;EACd,YAAY;EACZ,cAAc;CAChB,CAAC;CACD,OAAO;EACL,cAAc,OAAO,OAAO,eAAe,UAAU,YAAY,OAAO,OAAO,UAAU;EACzF,gBAAgB,OAAO,OAAO,eAAe,UAAU,cAAc,OAAO,OAAO,UAAU;EAC7F,cAAc,OAAO,eAAe,UAAU,YAAY,OAAO,UAAU;EAC3E,aAAa,UAAU,MAAM;EAC7B,gBAAgB,UAAU,SAAS;CACrC;AACF"}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createPoTelemetry } from "@agimon-ai/po-telemetry";
|
|
2
2
|
//#region src/services/telemetry/index.ts
|
|
3
3
|
const SERVICE_NAME = "po-ui";
|
|
4
4
|
const PACKAGE_NAME = "@agimon-ai/po-ui";
|
|
5
5
|
const UI_EVENT = {
|
|
6
|
-
leaderContributionRejected: "
|
|
7
|
-
leaderCommandUnavailable: "
|
|
8
|
-
leaderActionUnavailable: "
|
|
9
|
-
themeApplyFailed: "
|
|
10
|
-
shellInstalled: "
|
|
11
|
-
eventLoopStalled: "
|
|
6
|
+
leaderContributionRejected: "po_pi_ui.leader_contribution_rejected",
|
|
7
|
+
leaderCommandUnavailable: "po_pi_ui.leader_command_unavailable",
|
|
8
|
+
leaderActionUnavailable: "po_pi_ui.leader_action_unavailable",
|
|
9
|
+
themeApplyFailed: "po_pi_ui.theme_apply_failed",
|
|
10
|
+
shellInstalled: "po_pi_ui.shell_installed",
|
|
11
|
+
eventLoopStalled: "po_pi_ui.event_loop_stalled"
|
|
12
12
|
};
|
|
13
13
|
function createUiTelemetry(options = {}) {
|
|
14
|
-
const telemetry =
|
|
14
|
+
const telemetry = createPoTelemetry({
|
|
15
15
|
serviceName: SERVICE_NAME,
|
|
16
16
|
packageName: PACKAGE_NAME,
|
|
17
17
|
cwd: options.cwd,
|
|
@@ -33,4 +33,4 @@ function createUiTelemetry(options = {}) {
|
|
|
33
33
|
//#endregion
|
|
34
34
|
export { createUiTelemetry as n, UI_EVENT as t };
|
|
35
35
|
|
|
36
|
-
//# sourceMappingURL=telemetry
|
|
36
|
+
//# sourceMappingURL=telemetry-RiFqd0cg.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry-RiFqd0cg.mjs","names":[],"sources":["../src/services/telemetry/index.ts"],"sourcesContent":["import { createPoTelemetry, type PoTelemetry, type PoTelemetryOptions } from '@agimon-ai/po-telemetry';\n\nconst SERVICE_NAME = 'po-ui';\nconst PACKAGE_NAME = '@agimon-ai/po-ui';\n\nexport const UI_EVENT = {\n leaderContributionRejected: 'po_pi_ui.leader_contribution_rejected',\n leaderCommandUnavailable: 'po_pi_ui.leader_command_unavailable',\n leaderActionUnavailable: 'po_pi_ui.leader_action_unavailable',\n themeApplyFailed: 'po_pi_ui.theme_apply_failed',\n shellInstalled: 'po_pi_ui.shell_installed',\n eventLoopStalled: 'po_pi_ui.event_loop_stalled',\n} as const;\n\nexport type UiEventName = (typeof UI_EVENT)[keyof typeof UI_EVENT];\nexport type UiEventAttributes = Record<string, string | number | boolean>;\n\nexport interface UiTelemetryOptions {\n cwd?: string;\n workspaceRoot?: string;\n env?: NodeJS.ProcessEnv;\n telemetryFactory?: NonNullable<PoTelemetryOptions['telemetryFactory']>;\n warn?: (message: string) => void;\n}\n\nexport interface UiTelemetry {\n recordError(event: UiEventName, error: unknown, attributes?: UiEventAttributes): Promise<void>;\n recordWarning(event: UiEventName, error: unknown, attributes?: UiEventAttributes): Promise<void>;\n recordEvent(event: UiEventName, attributes?: UiEventAttributes): Promise<void>;\n flush(): Promise<void>;\n shutdown(): Promise<void>;\n}\n\nexport function createUiTelemetry(options: UiTelemetryOptions = {}): UiTelemetry {\n const telemetry: PoTelemetry = createPoTelemetry({\n serviceName: SERVICE_NAME,\n packageName: PACKAGE_NAME,\n cwd: options.cwd,\n workspaceRoot: options.workspaceRoot,\n env: options.env,\n telemetryFactory: options.telemetryFactory,\n warn: options.warn,\n enableLogs: true,\n enableTraces: true,\n });\n return {\n recordError: (event, error, attributes) => telemetry.recordError(event, error, attributes),\n recordWarning: (event, error, attributes) => telemetry.recordWarning(event, error, attributes),\n recordEvent: (event, attributes) => telemetry.recordEvent(event, attributes),\n flush: () => telemetry.flush(),\n shutdown: () => telemetry.shutdown(),\n };\n}\n"],"mappings":";;AAEA,MAAM,eAAe;AACrB,MAAM,eAAe;AAErB,MAAa,WAAW;CACtB,4BAA4B;CAC5B,0BAA0B;CAC1B,yBAAyB;CACzB,kBAAkB;CAClB,gBAAgB;CAChB,kBAAkB;AACpB;AAqBA,SAAgB,kBAAkB,UAA8B,CAAC,GAAgB;CAC/E,MAAM,YAAyB,kBAAkB;EAC/C,aAAa;EACb,aAAa;EACb,KAAK,QAAQ;EACb,eAAe,QAAQ;EACvB,KAAK,QAAQ;EACb,kBAAkB,QAAQ;EAC1B,MAAM,QAAQ;EACd,YAAY;EACZ,cAAc;CAChB,CAAC;CACD,OAAO;EACL,cAAc,OAAO,OAAO,eAAe,UAAU,YAAY,OAAO,OAAO,UAAU;EACzF,gBAAgB,OAAO,OAAO,eAAe,UAAU,cAAc,OAAO,OAAO,UAAU;EAC7F,cAAc,OAAO,eAAe,UAAU,YAAY,OAAO,UAAU;EAC3E,aAAa,UAAU,MAAM;EAC7B,gBAAgB,UAAU,SAAS;CACrC;AACF"}
|
|
@@ -1,40 +1,8 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
1
|
import path from "node:path";
|
|
3
|
-
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { getOrThrow, nodeHost } from "@agimon-ai/po-core/platform";
|
|
4
|
+
//#region src/services/theme/defaultTheme.ts
|
|
4
5
|
const DEFAULT_THEME_NAME = "po-dark";
|
|
5
|
-
const AGENT_IDENTITY_COLORS = [
|
|
6
|
-
"accent",
|
|
7
|
-
"mdCode",
|
|
8
|
-
"toolDiffAdded",
|
|
9
|
-
"warning",
|
|
10
|
-
"mdHeading",
|
|
11
|
-
"syntaxNumber"
|
|
12
|
-
];
|
|
13
|
-
function agentIdentityColorIndex(identity) {
|
|
14
|
-
let hash = 2166136261;
|
|
15
|
-
for (const character of identity) {
|
|
16
|
-
hash ^= character.charCodeAt(0);
|
|
17
|
-
hash = Math.imul(hash, 16777619);
|
|
18
|
-
}
|
|
19
|
-
return (hash >>> 0) % AGENT_IDENTITY_COLORS.length;
|
|
20
|
-
}
|
|
21
|
-
/** Stable semantic color shared by every UI surface that identifies an agent run. */
|
|
22
|
-
function agentIdentityColor(identity) {
|
|
23
|
-
return AGENT_IDENTITY_COLORS[agentIdentityColorIndex(identity)];
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Prefer the identity's stable color, then walk the palette to avoid reusing a
|
|
27
|
-
* color already visible on the same surface. Once every color is occupied,
|
|
28
|
-
* reuse the stable preference rather than changing existing assignments.
|
|
29
|
-
*/
|
|
30
|
-
function allocateAgentIdentityColor(identity, occupied) {
|
|
31
|
-
const preferredIndex = agentIdentityColorIndex(identity);
|
|
32
|
-
for (let offset = 0; offset < AGENT_IDENTITY_COLORS.length; offset++) {
|
|
33
|
-
const color = AGENT_IDENTITY_COLORS[(preferredIndex + offset) % AGENT_IDENTITY_COLORS.length];
|
|
34
|
-
if (!occupied.has(color)) return color;
|
|
35
|
-
}
|
|
36
|
-
return AGENT_IDENTITY_COLORS[preferredIndex];
|
|
37
|
-
}
|
|
38
6
|
const DEFAULT_THEME = {
|
|
39
7
|
$schema: "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
|
|
40
8
|
name: DEFAULT_THEME_NAME,
|
|
@@ -123,12 +91,64 @@ const DEFAULT_THEME = {
|
|
|
123
91
|
infoBg: "#3b3526"
|
|
124
92
|
}
|
|
125
93
|
};
|
|
94
|
+
/** Where the default theme goes in `temporaryDirectory`, and its file content. */
|
|
95
|
+
function defaultThemeFile(temporaryDirectory) {
|
|
96
|
+
return {
|
|
97
|
+
path: path.join(temporaryDirectory, `${DEFAULT_THEME_NAME}.json`),
|
|
98
|
+
content: `${JSON.stringify(DEFAULT_THEME, null, 2)}\n`
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/services/theme/sync.ts
|
|
103
|
+
/** @deprecated Node-only sync shim; use writeDefaultThemeFile. Removed in M2. */
|
|
126
104
|
async function writeDefaultTheme(temporaryDirectory) {
|
|
127
|
-
const
|
|
128
|
-
await fs.promises.writeFile(
|
|
129
|
-
return
|
|
105
|
+
const { path, content } = defaultThemeFile(temporaryDirectory);
|
|
106
|
+
await fs.promises.writeFile(path, content, { mode: 384 });
|
|
107
|
+
return path;
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/services/theme/index.ts
|
|
111
|
+
const AGENT_IDENTITY_COLORS = [
|
|
112
|
+
"accent",
|
|
113
|
+
"mdCode",
|
|
114
|
+
"toolDiffAdded",
|
|
115
|
+
"warning",
|
|
116
|
+
"mdHeading",
|
|
117
|
+
"syntaxNumber"
|
|
118
|
+
];
|
|
119
|
+
function agentIdentityColorIndex(identity) {
|
|
120
|
+
let hash = 2166136261;
|
|
121
|
+
for (const character of identity) {
|
|
122
|
+
hash ^= character.charCodeAt(0);
|
|
123
|
+
hash = Math.imul(hash, 16777619);
|
|
124
|
+
}
|
|
125
|
+
return (hash >>> 0) % AGENT_IDENTITY_COLORS.length;
|
|
126
|
+
}
|
|
127
|
+
/** Stable semantic color shared by every UI surface that identifies an agent run. */
|
|
128
|
+
function agentIdentityColor(identity) {
|
|
129
|
+
return AGENT_IDENTITY_COLORS[agentIdentityColorIndex(identity)];
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Prefer the identity's stable color, then walk the palette to avoid reusing a
|
|
133
|
+
* color already visible on the same surface. Once every color is occupied,
|
|
134
|
+
* reuse the stable preference rather than changing existing assignments.
|
|
135
|
+
*/
|
|
136
|
+
function allocateAgentIdentityColor(identity, occupied) {
|
|
137
|
+
const preferredIndex = agentIdentityColorIndex(identity);
|
|
138
|
+
for (let offset = 0; offset < AGENT_IDENTITY_COLORS.length; offset++) {
|
|
139
|
+
const color = AGENT_IDENTITY_COLORS[(preferredIndex + offset) % AGENT_IDENTITY_COLORS.length];
|
|
140
|
+
if (!occupied.has(color)) return color;
|
|
141
|
+
}
|
|
142
|
+
return AGENT_IDENTITY_COLORS[preferredIndex];
|
|
143
|
+
}
|
|
144
|
+
/** Writes the default theme into `temporaryDirectory` through `fs`, readable by its owner only, and returns its path. */
|
|
145
|
+
async function writeDefaultThemeFile(fs, temporaryDirectory, context) {
|
|
146
|
+
const { path, content } = defaultThemeFile(temporaryDirectory);
|
|
147
|
+
getOrThrow(await fs.writeFile(path, content, context));
|
|
148
|
+
await nodeHost.setMode(path, 384);
|
|
149
|
+
return path;
|
|
130
150
|
}
|
|
131
151
|
//#endregion
|
|
132
|
-
export {
|
|
152
|
+
export { DEFAULT_THEME as a, writeDefaultTheme as i, allocateAgentIdentityColor as n, DEFAULT_THEME_NAME as o, writeDefaultThemeFile as r, agentIdentityColor as t };
|
|
133
153
|
|
|
134
|
-
//# sourceMappingURL=theme-
|
|
154
|
+
//# sourceMappingURL=theme-BX_hfErv.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-BX_hfErv.mjs","names":[],"sources":["../src/services/theme/defaultTheme.ts","../src/services/theme/sync.ts","../src/services/theme/index.ts"],"sourcesContent":["import path from 'node:path';\n\nexport const DEFAULT_THEME_NAME = 'po-dark';\n\n/** The theme file is readable by its owner only. */\nexport const DEFAULT_THEME_FILE_MODE = 0o600;\n\nexport const DEFAULT_THEME = {\n $schema:\n 'https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json',\n name: DEFAULT_THEME_NAME,\n // Po Jade's quiet ink surfaces and green identity. Syntax colors retain\n // their meaning instead of making every operation a brand accent.\n vars: {\n bg: '#1c1c1c',\n bgAlt: '#171717',\n bgActive: '#2b2b2b',\n bgHover: '#2b2b2b',\n fg: '#dedede',\n fgAlt: '#b0b0b0',\n grey: '#343434',\n greyAlt: '#555555',\n blue: '#8cb5cc',\n darkBlue: '#284b64',\n cyan: '#8fc8c7',\n green: '#81cca3',\n lightGreen: '#a5d9b5',\n teal: '#8ec1ab',\n orange: '#d3ad83',\n red: '#e99185',\n violet: '#b0a0ce',\n magenta: '#c19aae',\n yellow: '#d6be82',\n lightYellow: '#e1cf9e',\n comment: '#a4a4a4',\n // Recessed diff bands retain readable syntax and quiet foreground text.\n diffAddedBg: '#152d20',\n diffRemovedBg: '#35201b',\n },\n colors: {\n accent: 'green',\n border: 'greyAlt',\n borderAccent: 'green',\n borderMuted: 'grey',\n success: 'green',\n error: 'red',\n warning: 'yellow',\n muted: 'fgAlt',\n dim: 'comment',\n text: 'fg',\n thinkingText: 'fgAlt',\n\n selectedBg: 'bgHover',\n userMessageBg: 'bgActive',\n userMessageText: 'fg',\n customMessageBg: '#292c2a',\n customMessageText: 'fg',\n customMessageLabel: 'green',\n toolPendingBg: 'bgAlt',\n // Doubles as the added/removed band behind a diff row, so it stays a wash\n // rather than a fill: syntax highlighting has to remain legible on top.\n toolSuccessBg: 'diffAddedBg',\n toolErrorBg: 'diffRemovedBg',\n toolTitle: 'green',\n toolOutput: 'fg',\n\n mdHeading: 'magenta',\n mdLink: 'blue',\n mdLinkUrl: 'comment',\n mdCode: 'cyan',\n mdCodeBlock: 'lightYellow',\n mdCodeBlockBorder: 'greyAlt',\n mdQuote: 'fgAlt',\n mdQuoteBorder: 'greyAlt',\n mdHr: 'greyAlt',\n mdListBullet: 'green',\n\n toolDiffAdded: 'lightGreen',\n toolDiffRemoved: 'red',\n toolDiffContext: 'fgAlt',\n\n syntaxComment: 'comment',\n syntaxKeyword: 'blue',\n syntaxFunction: 'magenta',\n syntaxVariable: 'violet',\n syntaxString: 'green',\n syntaxNumber: 'orange',\n syntaxType: 'yellow',\n syntaxOperator: 'orange',\n syntaxPunctuation: 'fgAlt',\n\n // A rising ramp from quiet ink through the semantic syntax ladder.\n thinkingOff: 'grey',\n thinkingMinimal: 'fgAlt',\n thinkingLow: 'darkBlue',\n thinkingMedium: 'blue',\n thinkingHigh: 'teal',\n thinkingXhigh: 'magenta',\n thinkingMax: 'red',\n\n bashMode: 'lightGreen',\n },\n export: {\n pageBg: 'bg',\n cardBg: 'bgAlt',\n infoBg: '#3b3526',\n },\n} as const;\n\n/** Where the default theme goes in `temporaryDirectory`, and its file content. */\nexport function defaultThemeFile(temporaryDirectory: string): { path: string; content: string } {\n return {\n path: path.join(temporaryDirectory, `${DEFAULT_THEME_NAME}.json`),\n content: `${JSON.stringify(DEFAULT_THEME, null, 2)}\\n`,\n };\n}\n","import fs from 'node:fs';\n\nimport { DEFAULT_THEME_FILE_MODE, defaultThemeFile } from './defaultTheme';\n\n/** @deprecated Node-only sync shim; use writeDefaultThemeFile. Removed in M2. */\nexport async function writeDefaultTheme(temporaryDirectory: string): Promise<string> {\n const { path, content } = defaultThemeFile(temporaryDirectory);\n await fs.promises.writeFile(path, content, { mode: DEFAULT_THEME_FILE_MODE });\n return path;\n}\n","import type { FooterTextColor } from '@agimon-ai/po-core/footer';\nimport { type FileSystem, getOrThrow, nodeHost } from '@agimon-ai/po-core/platform';\n\nimport { DEFAULT_THEME_FILE_MODE, defaultThemeFile } from './defaultTheme';\n\nexport { DEFAULT_THEME, DEFAULT_THEME_NAME } from './defaultTheme';\nexport { writeDefaultTheme } from './sync';\n\nconst AGENT_IDENTITY_COLORS = [\n 'accent',\n 'mdCode',\n 'toolDiffAdded',\n 'warning',\n 'mdHeading',\n 'syntaxNumber',\n] as const satisfies readonly FooterTextColor[];\n\nfunction agentIdentityColorIndex(identity: string): number {\n let hash = 2_166_136_261;\n for (const character of identity) {\n hash ^= character.charCodeAt(0);\n hash = Math.imul(hash, 16_777_619);\n }\n return (hash >>> 0) % AGENT_IDENTITY_COLORS.length;\n}\n\n/** Stable semantic color shared by every UI surface that identifies an agent run. */\nexport function agentIdentityColor(identity: string): FooterTextColor {\n return AGENT_IDENTITY_COLORS[agentIdentityColorIndex(identity)]!;\n}\n\n/**\n * Prefer the identity's stable color, then walk the palette to avoid reusing a\n * color already visible on the same surface. Once every color is occupied,\n * reuse the stable preference rather than changing existing assignments.\n */\nexport function allocateAgentIdentityColor(identity: string, occupied: ReadonlySet<FooterTextColor>): FooterTextColor {\n const preferredIndex = agentIdentityColorIndex(identity);\n for (let offset = 0; offset < AGENT_IDENTITY_COLORS.length; offset++) {\n const color = AGENT_IDENTITY_COLORS[(preferredIndex + offset) % AGENT_IDENTITY_COLORS.length]!;\n if (!occupied.has(color)) return color;\n }\n return AGENT_IDENTITY_COLORS[preferredIndex]!;\n}\n\n/** Writes the default theme into `temporaryDirectory` through `fs`, readable by its owner only, and returns its path. */\nexport async function writeDefaultThemeFile(\n fs: FileSystem,\n temporaryDirectory: string,\n context: Parameters<FileSystem['writeFile']>[2],\n): Promise<string> {\n const { path, content } = defaultThemeFile(temporaryDirectory);\n getOrThrow(await fs.writeFile(path, content, context));\n await nodeHost.setMode(path, DEFAULT_THEME_FILE_MODE);\n return path;\n}\n"],"mappings":";;;;AAEA,MAAa,qBAAqB;AAKlC,MAAa,gBAAgB;CAC3B,SACE;CACF,MAAM;CAGN,MAAM;EACJ,IAAI;EACJ,OAAO;EACP,UAAU;EACV,SAAS;EACT,IAAI;EACJ,OAAO;EACP,MAAM;EACN,SAAS;EACT,MAAM;EACN,UAAU;EACV,MAAM;EACN,OAAO;EACP,YAAY;EACZ,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;EACR,SAAS;EACT,QAAQ;EACR,aAAa;EACb,SAAS;EAET,aAAa;EACb,eAAe;CACjB;CACA,QAAQ;EACN,QAAQ;EACR,QAAQ;EACR,cAAc;EACd,aAAa;EACb,SAAS;EACT,OAAO;EACP,SAAS;EACT,OAAO;EACP,KAAK;EACL,MAAM;EACN,cAAc;EAEd,YAAY;EACZ,eAAe;EACf,iBAAiB;EACjB,iBAAiB;EACjB,mBAAmB;EACnB,oBAAoB;EACpB,eAAe;EAGf,eAAe;EACf,aAAa;EACb,WAAW;EACX,YAAY;EAEZ,WAAW;EACX,QAAQ;EACR,WAAW;EACX,QAAQ;EACR,aAAa;EACb,mBAAmB;EACnB,SAAS;EACT,eAAe;EACf,MAAM;EACN,cAAc;EAEd,eAAe;EACf,iBAAiB;EACjB,iBAAiB;EAEjB,eAAe;EACf,eAAe;EACf,gBAAgB;EAChB,gBAAgB;EAChB,cAAc;EACd,cAAc;EACd,YAAY;EACZ,gBAAgB;EAChB,mBAAmB;EAGnB,aAAa;EACb,iBAAiB;EACjB,aAAa;EACb,gBAAgB;EAChB,cAAc;EACd,eAAe;EACf,aAAa;EAEb,UAAU;CACZ;CACA,QAAQ;EACN,QAAQ;EACR,QAAQ;EACR,QAAQ;CACV;AACF;;AAGA,SAAgB,iBAAiB,oBAA+D;CAC9F,OAAO;EACL,MAAM,KAAK,KAAK,oBAAoB,GAAG,mBAAmB,MAAM;EAChE,SAAS,GAAG,KAAK,UAAU,eAAe,MAAM,CAAC,EAAE;CACrD;AACF;;;;AC9GA,eAAsB,kBAAkB,oBAA6C;CACnF,MAAM,EAAE,MAAM,YAAY,iBAAiB,kBAAkB;CAC7D,MAAM,GAAG,SAAS,UAAU,MAAM,SAAS,EAAE,MAAA,IAA8B,CAAC;CAC5E,OAAO;AACT;;;ACDA,MAAM,wBAAwB;CAC5B;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,wBAAwB,UAA0B;CACzD,IAAI,OAAO;CACX,KAAK,MAAM,aAAa,UAAU;EAChC,QAAQ,UAAU,WAAW,CAAC;EAC9B,OAAO,KAAK,KAAK,MAAM,QAAU;CACnC;CACA,QAAQ,SAAS,KAAK,sBAAsB;AAC9C;;AAGA,SAAgB,mBAAmB,UAAmC;CACpE,OAAO,sBAAsB,wBAAwB,QAAQ;AAC/D;;;;;;AAOA,SAAgB,2BAA2B,UAAkB,UAAyD;CACpH,MAAM,iBAAiB,wBAAwB,QAAQ;CACvD,KAAK,IAAI,SAAS,GAAG,SAAS,sBAAsB,QAAQ,UAAU;EACpE,MAAM,QAAQ,uBAAuB,iBAAiB,UAAU,sBAAsB;EACtF,IAAI,CAAC,SAAS,IAAI,KAAK,GAAG,OAAO;CACnC;CACA,OAAO,sBAAsB;AAC/B;;AAGA,eAAsB,sBACpB,IACA,oBACA,SACiB;CACjB,MAAM,EAAE,MAAM,YAAY,iBAAiB,kBAAkB;CAC7D,WAAW,MAAM,GAAG,UAAU,MAAM,SAAS,OAAO,CAAC;CACrD,MAAM,SAAS,QAAQ,MAAA,GAA6B;CACpD,OAAO;AACT"}
|
|
@@ -1,43 +1,11 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
|
|
2
|
-
let node_fs = require("node:fs");
|
|
3
|
-
node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
|
|
4
2
|
let node_path = require("node:path");
|
|
5
3
|
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
6
|
-
|
|
4
|
+
let node_fs = require("node:fs");
|
|
5
|
+
node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
|
|
6
|
+
let _agimon_ai_po_core_platform = require("@agimon-ai/po-core/platform");
|
|
7
|
+
//#region src/services/theme/defaultTheme.ts
|
|
7
8
|
const DEFAULT_THEME_NAME = "po-dark";
|
|
8
|
-
const AGENT_IDENTITY_COLORS = [
|
|
9
|
-
"accent",
|
|
10
|
-
"mdCode",
|
|
11
|
-
"toolDiffAdded",
|
|
12
|
-
"warning",
|
|
13
|
-
"mdHeading",
|
|
14
|
-
"syntaxNumber"
|
|
15
|
-
];
|
|
16
|
-
function agentIdentityColorIndex(identity) {
|
|
17
|
-
let hash = 2166136261;
|
|
18
|
-
for (const character of identity) {
|
|
19
|
-
hash ^= character.charCodeAt(0);
|
|
20
|
-
hash = Math.imul(hash, 16777619);
|
|
21
|
-
}
|
|
22
|
-
return (hash >>> 0) % AGENT_IDENTITY_COLORS.length;
|
|
23
|
-
}
|
|
24
|
-
/** Stable semantic color shared by every UI surface that identifies an agent run. */
|
|
25
|
-
function agentIdentityColor(identity) {
|
|
26
|
-
return AGENT_IDENTITY_COLORS[agentIdentityColorIndex(identity)];
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Prefer the identity's stable color, then walk the palette to avoid reusing a
|
|
30
|
-
* color already visible on the same surface. Once every color is occupied,
|
|
31
|
-
* reuse the stable preference rather than changing existing assignments.
|
|
32
|
-
*/
|
|
33
|
-
function allocateAgentIdentityColor(identity, occupied) {
|
|
34
|
-
const preferredIndex = agentIdentityColorIndex(identity);
|
|
35
|
-
for (let offset = 0; offset < AGENT_IDENTITY_COLORS.length; offset++) {
|
|
36
|
-
const color = AGENT_IDENTITY_COLORS[(preferredIndex + offset) % AGENT_IDENTITY_COLORS.length];
|
|
37
|
-
if (!occupied.has(color)) return color;
|
|
38
|
-
}
|
|
39
|
-
return AGENT_IDENTITY_COLORS[preferredIndex];
|
|
40
|
-
}
|
|
41
9
|
const DEFAULT_THEME = {
|
|
42
10
|
$schema: "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
|
|
43
11
|
name: DEFAULT_THEME_NAME,
|
|
@@ -126,10 +94,62 @@ const DEFAULT_THEME = {
|
|
|
126
94
|
infoBg: "#3b3526"
|
|
127
95
|
}
|
|
128
96
|
};
|
|
97
|
+
/** Where the default theme goes in `temporaryDirectory`, and its file content. */
|
|
98
|
+
function defaultThemeFile(temporaryDirectory) {
|
|
99
|
+
return {
|
|
100
|
+
path: node_path.default.join(temporaryDirectory, `${DEFAULT_THEME_NAME}.json`),
|
|
101
|
+
content: `${JSON.stringify(DEFAULT_THEME, null, 2)}\n`
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/services/theme/sync.ts
|
|
106
|
+
/** @deprecated Node-only sync shim; use writeDefaultThemeFile. Removed in M2. */
|
|
129
107
|
async function writeDefaultTheme(temporaryDirectory) {
|
|
130
|
-
const
|
|
131
|
-
await node_fs.default.promises.writeFile(
|
|
132
|
-
return
|
|
108
|
+
const { path, content } = defaultThemeFile(temporaryDirectory);
|
|
109
|
+
await node_fs.default.promises.writeFile(path, content, { mode: 384 });
|
|
110
|
+
return path;
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
//#region src/services/theme/index.ts
|
|
114
|
+
const AGENT_IDENTITY_COLORS = [
|
|
115
|
+
"accent",
|
|
116
|
+
"mdCode",
|
|
117
|
+
"toolDiffAdded",
|
|
118
|
+
"warning",
|
|
119
|
+
"mdHeading",
|
|
120
|
+
"syntaxNumber"
|
|
121
|
+
];
|
|
122
|
+
function agentIdentityColorIndex(identity) {
|
|
123
|
+
let hash = 2166136261;
|
|
124
|
+
for (const character of identity) {
|
|
125
|
+
hash ^= character.charCodeAt(0);
|
|
126
|
+
hash = Math.imul(hash, 16777619);
|
|
127
|
+
}
|
|
128
|
+
return (hash >>> 0) % AGENT_IDENTITY_COLORS.length;
|
|
129
|
+
}
|
|
130
|
+
/** Stable semantic color shared by every UI surface that identifies an agent run. */
|
|
131
|
+
function agentIdentityColor(identity) {
|
|
132
|
+
return AGENT_IDENTITY_COLORS[agentIdentityColorIndex(identity)];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Prefer the identity's stable color, then walk the palette to avoid reusing a
|
|
136
|
+
* color already visible on the same surface. Once every color is occupied,
|
|
137
|
+
* reuse the stable preference rather than changing existing assignments.
|
|
138
|
+
*/
|
|
139
|
+
function allocateAgentIdentityColor(identity, occupied) {
|
|
140
|
+
const preferredIndex = agentIdentityColorIndex(identity);
|
|
141
|
+
for (let offset = 0; offset < AGENT_IDENTITY_COLORS.length; offset++) {
|
|
142
|
+
const color = AGENT_IDENTITY_COLORS[(preferredIndex + offset) % AGENT_IDENTITY_COLORS.length];
|
|
143
|
+
if (!occupied.has(color)) return color;
|
|
144
|
+
}
|
|
145
|
+
return AGENT_IDENTITY_COLORS[preferredIndex];
|
|
146
|
+
}
|
|
147
|
+
/** Writes the default theme into `temporaryDirectory` through `fs`, readable by its owner only, and returns its path. */
|
|
148
|
+
async function writeDefaultThemeFile(fs, temporaryDirectory, context) {
|
|
149
|
+
const { path, content } = defaultThemeFile(temporaryDirectory);
|
|
150
|
+
(0, _agimon_ai_po_core_platform.getOrThrow)(await fs.writeFile(path, content, context));
|
|
151
|
+
await _agimon_ai_po_core_platform.nodeHost.setMode(path, 384);
|
|
152
|
+
return path;
|
|
133
153
|
}
|
|
134
154
|
//#endregion
|
|
135
155
|
Object.defineProperty(exports, "DEFAULT_THEME", {
|
|
@@ -162,5 +182,11 @@ Object.defineProperty(exports, "writeDefaultTheme", {
|
|
|
162
182
|
return writeDefaultTheme;
|
|
163
183
|
}
|
|
164
184
|
});
|
|
185
|
+
Object.defineProperty(exports, "writeDefaultThemeFile", {
|
|
186
|
+
enumerable: true,
|
|
187
|
+
get: function() {
|
|
188
|
+
return writeDefaultThemeFile;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
165
191
|
|
|
166
|
-
//# sourceMappingURL=theme-
|
|
192
|
+
//# sourceMappingURL=theme-BfC_GGQ4.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-BfC_GGQ4.cjs","names":["path","fs","nodeHost"],"sources":["../src/services/theme/defaultTheme.ts","../src/services/theme/sync.ts","../src/services/theme/index.ts"],"sourcesContent":["import path from 'node:path';\n\nexport const DEFAULT_THEME_NAME = 'po-dark';\n\n/** The theme file is readable by its owner only. */\nexport const DEFAULT_THEME_FILE_MODE = 0o600;\n\nexport const DEFAULT_THEME = {\n $schema:\n 'https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json',\n name: DEFAULT_THEME_NAME,\n // Po Jade's quiet ink surfaces and green identity. Syntax colors retain\n // their meaning instead of making every operation a brand accent.\n vars: {\n bg: '#1c1c1c',\n bgAlt: '#171717',\n bgActive: '#2b2b2b',\n bgHover: '#2b2b2b',\n fg: '#dedede',\n fgAlt: '#b0b0b0',\n grey: '#343434',\n greyAlt: '#555555',\n blue: '#8cb5cc',\n darkBlue: '#284b64',\n cyan: '#8fc8c7',\n green: '#81cca3',\n lightGreen: '#a5d9b5',\n teal: '#8ec1ab',\n orange: '#d3ad83',\n red: '#e99185',\n violet: '#b0a0ce',\n magenta: '#c19aae',\n yellow: '#d6be82',\n lightYellow: '#e1cf9e',\n comment: '#a4a4a4',\n // Recessed diff bands retain readable syntax and quiet foreground text.\n diffAddedBg: '#152d20',\n diffRemovedBg: '#35201b',\n },\n colors: {\n accent: 'green',\n border: 'greyAlt',\n borderAccent: 'green',\n borderMuted: 'grey',\n success: 'green',\n error: 'red',\n warning: 'yellow',\n muted: 'fgAlt',\n dim: 'comment',\n text: 'fg',\n thinkingText: 'fgAlt',\n\n selectedBg: 'bgHover',\n userMessageBg: 'bgActive',\n userMessageText: 'fg',\n customMessageBg: '#292c2a',\n customMessageText: 'fg',\n customMessageLabel: 'green',\n toolPendingBg: 'bgAlt',\n // Doubles as the added/removed band behind a diff row, so it stays a wash\n // rather than a fill: syntax highlighting has to remain legible on top.\n toolSuccessBg: 'diffAddedBg',\n toolErrorBg: 'diffRemovedBg',\n toolTitle: 'green',\n toolOutput: 'fg',\n\n mdHeading: 'magenta',\n mdLink: 'blue',\n mdLinkUrl: 'comment',\n mdCode: 'cyan',\n mdCodeBlock: 'lightYellow',\n mdCodeBlockBorder: 'greyAlt',\n mdQuote: 'fgAlt',\n mdQuoteBorder: 'greyAlt',\n mdHr: 'greyAlt',\n mdListBullet: 'green',\n\n toolDiffAdded: 'lightGreen',\n toolDiffRemoved: 'red',\n toolDiffContext: 'fgAlt',\n\n syntaxComment: 'comment',\n syntaxKeyword: 'blue',\n syntaxFunction: 'magenta',\n syntaxVariable: 'violet',\n syntaxString: 'green',\n syntaxNumber: 'orange',\n syntaxType: 'yellow',\n syntaxOperator: 'orange',\n syntaxPunctuation: 'fgAlt',\n\n // A rising ramp from quiet ink through the semantic syntax ladder.\n thinkingOff: 'grey',\n thinkingMinimal: 'fgAlt',\n thinkingLow: 'darkBlue',\n thinkingMedium: 'blue',\n thinkingHigh: 'teal',\n thinkingXhigh: 'magenta',\n thinkingMax: 'red',\n\n bashMode: 'lightGreen',\n },\n export: {\n pageBg: 'bg',\n cardBg: 'bgAlt',\n infoBg: '#3b3526',\n },\n} as const;\n\n/** Where the default theme goes in `temporaryDirectory`, and its file content. */\nexport function defaultThemeFile(temporaryDirectory: string): { path: string; content: string } {\n return {\n path: path.join(temporaryDirectory, `${DEFAULT_THEME_NAME}.json`),\n content: `${JSON.stringify(DEFAULT_THEME, null, 2)}\\n`,\n };\n}\n","import fs from 'node:fs';\n\nimport { DEFAULT_THEME_FILE_MODE, defaultThemeFile } from './defaultTheme';\n\n/** @deprecated Node-only sync shim; use writeDefaultThemeFile. Removed in M2. */\nexport async function writeDefaultTheme(temporaryDirectory: string): Promise<string> {\n const { path, content } = defaultThemeFile(temporaryDirectory);\n await fs.promises.writeFile(path, content, { mode: DEFAULT_THEME_FILE_MODE });\n return path;\n}\n","import type { FooterTextColor } from '@agimon-ai/po-core/footer';\nimport { type FileSystem, getOrThrow, nodeHost } from '@agimon-ai/po-core/platform';\n\nimport { DEFAULT_THEME_FILE_MODE, defaultThemeFile } from './defaultTheme';\n\nexport { DEFAULT_THEME, DEFAULT_THEME_NAME } from './defaultTheme';\nexport { writeDefaultTheme } from './sync';\n\nconst AGENT_IDENTITY_COLORS = [\n 'accent',\n 'mdCode',\n 'toolDiffAdded',\n 'warning',\n 'mdHeading',\n 'syntaxNumber',\n] as const satisfies readonly FooterTextColor[];\n\nfunction agentIdentityColorIndex(identity: string): number {\n let hash = 2_166_136_261;\n for (const character of identity) {\n hash ^= character.charCodeAt(0);\n hash = Math.imul(hash, 16_777_619);\n }\n return (hash >>> 0) % AGENT_IDENTITY_COLORS.length;\n}\n\n/** Stable semantic color shared by every UI surface that identifies an agent run. */\nexport function agentIdentityColor(identity: string): FooterTextColor {\n return AGENT_IDENTITY_COLORS[agentIdentityColorIndex(identity)]!;\n}\n\n/**\n * Prefer the identity's stable color, then walk the palette to avoid reusing a\n * color already visible on the same surface. Once every color is occupied,\n * reuse the stable preference rather than changing existing assignments.\n */\nexport function allocateAgentIdentityColor(identity: string, occupied: ReadonlySet<FooterTextColor>): FooterTextColor {\n const preferredIndex = agentIdentityColorIndex(identity);\n for (let offset = 0; offset < AGENT_IDENTITY_COLORS.length; offset++) {\n const color = AGENT_IDENTITY_COLORS[(preferredIndex + offset) % AGENT_IDENTITY_COLORS.length]!;\n if (!occupied.has(color)) return color;\n }\n return AGENT_IDENTITY_COLORS[preferredIndex]!;\n}\n\n/** Writes the default theme into `temporaryDirectory` through `fs`, readable by its owner only, and returns its path. */\nexport async function writeDefaultThemeFile(\n fs: FileSystem,\n temporaryDirectory: string,\n context: Parameters<FileSystem['writeFile']>[2],\n): Promise<string> {\n const { path, content } = defaultThemeFile(temporaryDirectory);\n getOrThrow(await fs.writeFile(path, content, context));\n await nodeHost.setMode(path, DEFAULT_THEME_FILE_MODE);\n return path;\n}\n"],"mappings":";;;;;;;AAEA,MAAa,qBAAqB;AAKlC,MAAa,gBAAgB;CAC3B,SACE;CACF,MAAM;CAGN,MAAM;EACJ,IAAI;EACJ,OAAO;EACP,UAAU;EACV,SAAS;EACT,IAAI;EACJ,OAAO;EACP,MAAM;EACN,SAAS;EACT,MAAM;EACN,UAAU;EACV,MAAM;EACN,OAAO;EACP,YAAY;EACZ,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;EACR,SAAS;EACT,QAAQ;EACR,aAAa;EACb,SAAS;EAET,aAAa;EACb,eAAe;CACjB;CACA,QAAQ;EACN,QAAQ;EACR,QAAQ;EACR,cAAc;EACd,aAAa;EACb,SAAS;EACT,OAAO;EACP,SAAS;EACT,OAAO;EACP,KAAK;EACL,MAAM;EACN,cAAc;EAEd,YAAY;EACZ,eAAe;EACf,iBAAiB;EACjB,iBAAiB;EACjB,mBAAmB;EACnB,oBAAoB;EACpB,eAAe;EAGf,eAAe;EACf,aAAa;EACb,WAAW;EACX,YAAY;EAEZ,WAAW;EACX,QAAQ;EACR,WAAW;EACX,QAAQ;EACR,aAAa;EACb,mBAAmB;EACnB,SAAS;EACT,eAAe;EACf,MAAM;EACN,cAAc;EAEd,eAAe;EACf,iBAAiB;EACjB,iBAAiB;EAEjB,eAAe;EACf,eAAe;EACf,gBAAgB;EAChB,gBAAgB;EAChB,cAAc;EACd,cAAc;EACd,YAAY;EACZ,gBAAgB;EAChB,mBAAmB;EAGnB,aAAa;EACb,iBAAiB;EACjB,aAAa;EACb,gBAAgB;EAChB,cAAc;EACd,eAAe;EACf,aAAa;EAEb,UAAU;CACZ;CACA,QAAQ;EACN,QAAQ;EACR,QAAQ;EACR,QAAQ;CACV;AACF;;AAGA,SAAgB,iBAAiB,oBAA+D;CAC9F,OAAO;EACL,MAAMA,UAAAA,QAAK,KAAK,oBAAoB,GAAG,mBAAmB,MAAM;EAChE,SAAS,GAAG,KAAK,UAAU,eAAe,MAAM,CAAC,EAAE;CACrD;AACF;;;;AC9GA,eAAsB,kBAAkB,oBAA6C;CACnF,MAAM,EAAE,MAAM,YAAY,iBAAiB,kBAAkB;CAC7D,MAAMC,QAAAA,QAAG,SAAS,UAAU,MAAM,SAAS,EAAE,MAAA,IAA8B,CAAC;CAC5E,OAAO;AACT;;;ACDA,MAAM,wBAAwB;CAC5B;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,wBAAwB,UAA0B;CACzD,IAAI,OAAO;CACX,KAAK,MAAM,aAAa,UAAU;EAChC,QAAQ,UAAU,WAAW,CAAC;EAC9B,OAAO,KAAK,KAAK,MAAM,QAAU;CACnC;CACA,QAAQ,SAAS,KAAK,sBAAsB;AAC9C;;AAGA,SAAgB,mBAAmB,UAAmC;CACpE,OAAO,sBAAsB,wBAAwB,QAAQ;AAC/D;;;;;;AAOA,SAAgB,2BAA2B,UAAkB,UAAyD;CACpH,MAAM,iBAAiB,wBAAwB,QAAQ;CACvD,KAAK,IAAI,SAAS,GAAG,SAAS,sBAAsB,QAAQ,UAAU;EACpE,MAAM,QAAQ,uBAAuB,iBAAiB,UAAU,sBAAsB;EACtF,IAAI,CAAC,SAAS,IAAI,KAAK,GAAG,OAAO;CACnC;CACA,OAAO,sBAAsB;AAC/B;;AAGA,eAAsB,sBACpB,IACA,oBACA,SACiB;CACjB,MAAM,EAAE,MAAM,YAAY,iBAAiB,kBAAkB;CAC7D,CAAA,GAAA,4BAAA,WAAA,CAAW,MAAM,GAAG,UAAU,MAAM,SAAS,OAAO,CAAC;CACrD,MAAMC,4BAAAA,SAAS,QAAQ,MAAA,GAA6B;CACpD,OAAO;AACT"}
|
package/dist/theme.cjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_theme = require("./theme-
|
|
2
|
+
const require_theme = require("./theme-BfC_GGQ4.cjs");
|
|
3
3
|
exports.DEFAULT_THEME = require_theme.DEFAULT_THEME;
|
|
4
4
|
exports.DEFAULT_THEME_NAME = require_theme.DEFAULT_THEME_NAME;
|
|
5
5
|
exports.agentIdentityColor = require_theme.agentIdentityColor;
|
|
6
6
|
exports.allocateAgentIdentityColor = require_theme.allocateAgentIdentityColor;
|
|
7
7
|
exports.writeDefaultTheme = require_theme.writeDefaultTheme;
|
|
8
|
+
exports.writeDefaultThemeFile = require_theme.writeDefaultThemeFile;
|