codexmate 0.0.22 → 0.0.23

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.
@@ -58,6 +58,14 @@ const DICT = Object.freeze({
58
58
  'common.notExistsWillCreateOnApply': '不存在,将在应用时创建',
59
59
  'common.notExistsWillCreateOnSave': '不存在,将在保存时创建',
60
60
  'common.none': '暂无',
61
+ 'cli.missing.title': '{name} CLI 未安装',
62
+ 'cli.missing.subtitle': '请先安装 {name} CLI 后再继续使用此页面。',
63
+ 'cli.missing.openDocs': '打开安装指南',
64
+ 'cli.missing.commandAria': '{name} CLI 安装命令',
65
+
66
+ // Brand
67
+ 'brand.kicker.workspace': '工作区',
68
+ 'brand.subtitle.localConfigSessionsWorkspace': '本地配置与会话工作区',
61
69
 
62
70
  // Confirm dialog
63
71
  'confirm.aria': '确认操作',
@@ -601,6 +609,105 @@ const DICT = Object.freeze({
601
609
  'usage.range.7d.short': '近 7 天',
602
610
  'usage.range.30d.short': '近 30 天',
603
611
  'orchestration.queueStats': '队列 {running} 运行中 · {queued} 等待中',
612
+ 'orchestration.hero.kicker': '任务编排',
613
+ 'orchestration.hero.title': '把需求拆成可执行步骤',
614
+ 'orchestration.hero.subtitle': '先写目标,再预览计划,再执行。',
615
+ 'orchestration.draft.reset': '重置草稿',
616
+ 'orchestration.summary.aria': '任务编排概览',
617
+ 'orchestration.summary.running': '运行中',
618
+ 'orchestration.summary.queued': '排队中',
619
+ 'orchestration.summary.runs': '运行记录',
620
+ 'orchestration.step1.title': '先把结果写清楚',
621
+ 'orchestration.step1.subtitle': '只写会影响执行的内容。',
622
+ 'orchestration.templates.title': '快捷示例',
623
+ 'orchestration.templates.reviewFix.label': '修 review + 回归',
624
+ 'orchestration.templates.reviewFix.target': '修复当前 PR review 评论,并补对应回归测试',
625
+ 'orchestration.templates.reviewFix.notes': '不要改动无关模块;需要给出验证结果',
626
+ 'orchestration.templates.reviewFix.followUps': '继续处理新增 review 评论\n最后更新 PR 摘要',
627
+ 'orchestration.templates.planOnly.label': '只做排查规划',
628
+ 'orchestration.templates.planOnly.target': '先排查问题根因并给出执行计划,不直接修改代码',
629
+ 'orchestration.templates.planOnly.notes': '优先定位根因、影响范围和风险点',
630
+ 'orchestration.templates.workflowBatch.label': 'Workflow 批处理',
631
+ 'orchestration.templates.workflowBatch.target': '用 Workflow 跑一组固定检查并整理结果',
632
+ 'orchestration.templates.workflowBatch.workflowIds': 'diagnose-config\nsafe-provider-switch',
633
+ 'orchestration.templates.workflowBatch.notes': '输出统一结论,避免重复描述',
634
+ 'orchestration.fields.target': '目标',
635
+ 'orchestration.fields.target.placeholder': '例如:修复当前 PR review 评论,并补对应回归测试;不要改无关模块',
636
+ 'orchestration.fields.target.hint': '一句话写清结果、边界和验收标准就够了。',
637
+ 'orchestration.engine.codex': 'Codex',
638
+ 'orchestration.engine.workflow': 'Workflow',
639
+ 'orchestration.runMode.write': '写入',
640
+ 'orchestration.runMode.readOnly': '只读',
641
+ 'orchestration.runMode.dryRun': '仅预演',
642
+ 'orchestration.pills.hasTitle': '标题已设',
643
+ 'orchestration.pills.workflowCount': 'Workflow {count}',
644
+ 'orchestration.pills.planNodes': '计划 {count} 节点',
645
+ 'orchestration.step2.title': '选择执行方式',
646
+ 'orchestration.step2.subtitle': '常用项默认展开,其余放高级设置。',
647
+ 'orchestration.fields.engine': '引擎',
648
+ 'orchestration.fields.runMode': '运行模式',
649
+ 'orchestration.advanced.title': '高级设置',
650
+ 'orchestration.fields.title': '标题',
651
+ 'orchestration.fields.title.placeholder': '可选,默认从目标自动提取',
652
+ 'orchestration.fields.notes': '说明',
653
+ 'orchestration.fields.notes.placeholder': '例如:不要重写现有架构,只做增量实现',
654
+ 'orchestration.fields.notes.hint': '补边界、禁区、风格要求或验证要求。',
655
+ 'orchestration.fields.followUps': '后续动作(每行一条)',
656
+ 'orchestration.fields.followUps.placeholder': '例如:\n继续处理 review 评论\n最后补回归测试',
657
+ 'orchestration.fields.concurrency': '并发',
658
+ 'orchestration.fields.concurrency.hint': '复杂任务先从 1~2 开始更稳。',
659
+ 'orchestration.fields.autoFixRounds': '自动修复',
660
+ 'orchestration.fields.autoFixRounds.hint': '失败后自动再试几轮。',
661
+ 'orchestration.fields.workflowIds': 'Workflow ID(每行一条)',
662
+ 'orchestration.fields.workflowIds.placeholder': '例如:\ndiagnose-config\nsafe-provider-switch',
663
+ 'orchestration.fields.workflowIds.hint': '仅 Workflow 模式需要。当前本地可用 {count} 个。',
664
+ 'orchestration.workflow.stepCount': '{count} 步',
665
+ 'orchestration.step3.title': '先预览,再执行',
666
+ 'orchestration.step3.subtitle': '先确认计划,再决定立即执行还是入队。',
667
+ 'orchestration.actions.planning': '规划中...',
668
+ 'orchestration.actions.previewOnly': '仅预览',
669
+ 'orchestration.actions.preparing': '准备中...',
670
+ 'orchestration.actions.generatePlan': '生成计划',
671
+ 'orchestration.actions.planAndRun': '计划并执行',
672
+ 'orchestration.actions.processing': '处理中...',
673
+ 'orchestration.actions.queueAndStart': '入队并开始',
674
+ 'orchestration.actions.caption': '“计划并执行”会在必要时自动刷新计划;批量任务用“入队并开始”。',
675
+ 'orchestration.stage.title': '有内容时再展开工作区',
676
+ 'orchestration.stage.subtitle': '先写目标,再预览计划。',
677
+ 'orchestration.stage.pill.target': '写目标',
678
+ 'orchestration.stage.pill.preview': '预览',
679
+ 'orchestration.stage.pill.run': '执行或入队',
680
+ 'orchestration.plan.title': '计划预览',
681
+ 'orchestration.plan.subtitle': '先确认节点、波次和依赖。',
682
+ 'orchestration.plan.summary.nodes': '节点数',
683
+ 'orchestration.plan.summary.waves': '波次',
684
+ 'orchestration.plan.summary.engine': '引擎',
685
+ 'orchestration.plan.node.write': 'write',
686
+ 'orchestration.plan.node.readOnly': 'read-only',
687
+ 'orchestration.labels.dependencies': '依赖:',
688
+ 'orchestration.labels.error': '错误:',
689
+ 'orchestration.workbench.title': '执行工作台',
690
+ 'orchestration.workbench.subtitle': '有内容时才展开。',
691
+ 'orchestration.queue.start': '开始队列',
692
+ 'orchestration.queue.starting': '启动中...',
693
+ 'orchestration.workbench.tabs.aria': '任务编排工作台视图',
694
+ 'orchestration.workbench.tabs.queue': '队列 {count}',
695
+ 'orchestration.workbench.tabs.runs': '运行记录 {count}',
696
+ 'orchestration.workbench.tabs.detail': '运行详情',
697
+ 'orchestration.queue.empty.title': '当前没有排队任务',
698
+ 'orchestration.queue.empty.subtitle': '批量任务可先入队,再启动队列。',
699
+ 'orchestration.runs.empty.title': '还没有运行记录',
700
+ 'orchestration.runs.empty.subtitle': '执行后会显示最近运行记录。',
701
+ 'orchestration.detail.refresh': '刷新详情',
702
+ 'orchestration.detail.retry': '重试',
703
+ 'orchestration.detail.retrying': '重试中...',
704
+ 'orchestration.detail.empty.title': '选择一条运行记录查看详情',
705
+ 'orchestration.detail.empty.subtitle': '这里会显示节点状态、摘要和日志。',
706
+ 'orchestration.detail.summary.status': '状态',
707
+ 'orchestration.detail.summary.duration': '耗时',
708
+ 'orchestration.detail.summary.nodes': '节点数',
709
+ 'orchestration.detail.summary.summary': '摘要',
710
+ 'orchestration.detail.node.meta': '{id} · attempts {attempts} · auto-fix {autoFix}',
604
711
  'skills.localLabel': '{target} / 本地 Skills',
605
712
  'skills.counts': '已装 {installed} · 可导入 {importable}',
606
713
 
@@ -834,6 +941,14 @@ const DICT = Object.freeze({
834
941
  'common.notExistsWillCreateOnApply': 'Not found. Will be created on apply.',
835
942
  'common.notExistsWillCreateOnSave': 'Not found. Will be created on save.',
836
943
  'common.none': 'None',
944
+ 'cli.missing.title': '{name} CLI not installed',
945
+ 'cli.missing.subtitle': 'Install {name} CLI before using this page.',
946
+ 'cli.missing.openDocs': 'Open install guide',
947
+ 'cli.missing.commandAria': '{name} CLI install command',
948
+
949
+ // Brand
950
+ 'brand.kicker.workspace': 'Workspace',
951
+ 'brand.subtitle.localConfigSessionsWorkspace': 'Local config & sessions workspace',
837
952
 
838
953
  // Confirm dialog
839
954
  'confirm.aria': 'Confirm action',
@@ -1377,6 +1492,105 @@ const DICT = Object.freeze({
1377
1492
  'usage.range.7d.short': 'Last 7 days',
1378
1493
  'usage.range.30d.short': 'Last 30 days',
1379
1494
  'orchestration.queueStats': 'Queue: {running} running · {queued} queued',
1495
+ 'orchestration.hero.kicker': 'Task orchestration',
1496
+ 'orchestration.hero.title': 'Turn goals into executable steps',
1497
+ 'orchestration.hero.subtitle': 'Write a target, preview a plan, then run.',
1498
+ 'orchestration.draft.reset': 'Reset draft',
1499
+ 'orchestration.summary.aria': 'Task orchestration summary',
1500
+ 'orchestration.summary.running': 'Running',
1501
+ 'orchestration.summary.queued': 'Queued',
1502
+ 'orchestration.summary.runs': 'Runs',
1503
+ 'orchestration.step1.title': 'Start with the outcome',
1504
+ 'orchestration.step1.subtitle': 'Only include what affects execution.',
1505
+ 'orchestration.templates.title': 'Quick examples',
1506
+ 'orchestration.templates.reviewFix.label': 'Review fixes + regression',
1507
+ 'orchestration.templates.reviewFix.target': 'Address current PR review comments and add regression tests',
1508
+ 'orchestration.templates.reviewFix.notes': 'Avoid unrelated refactors; provide verification results',
1509
+ 'orchestration.templates.reviewFix.followUps': 'Handle newly added review comments\nUpdate the PR summary',
1510
+ 'orchestration.templates.planOnly.label': 'Plan only',
1511
+ 'orchestration.templates.planOnly.target': 'Investigate root cause and propose an execution plan without changing code',
1512
+ 'orchestration.templates.planOnly.notes': 'Focus on root cause, blast radius, and risk areas',
1513
+ 'orchestration.templates.workflowBatch.label': 'Workflow batch',
1514
+ 'orchestration.templates.workflowBatch.target': 'Run a fixed set of checks using workflows and summarize results',
1515
+ 'orchestration.templates.workflowBatch.workflowIds': 'diagnose-config\nsafe-provider-switch',
1516
+ 'orchestration.templates.workflowBatch.notes': 'Produce a unified conclusion; avoid repetitive narration',
1517
+ 'orchestration.fields.target': 'Target',
1518
+ 'orchestration.fields.target.placeholder': 'e.g. Address PR review comments and add regression tests; avoid unrelated modules',
1519
+ 'orchestration.fields.target.hint': 'One sentence is enough: outcome, constraints, and acceptance criteria.',
1520
+ 'orchestration.engine.codex': 'Codex',
1521
+ 'orchestration.engine.workflow': 'Workflow',
1522
+ 'orchestration.runMode.write': 'Write',
1523
+ 'orchestration.runMode.readOnly': 'Read-only',
1524
+ 'orchestration.runMode.dryRun': 'Dry run',
1525
+ 'orchestration.pills.hasTitle': 'Title set',
1526
+ 'orchestration.pills.workflowCount': 'Workflows {count}',
1527
+ 'orchestration.pills.planNodes': 'Plan {count} nodes',
1528
+ 'orchestration.step2.title': 'Choose how to run',
1529
+ 'orchestration.step2.subtitle': 'Common options are visible; the rest lives under Advanced.',
1530
+ 'orchestration.fields.engine': 'Engine',
1531
+ 'orchestration.fields.runMode': 'Run mode',
1532
+ 'orchestration.advanced.title': 'Advanced',
1533
+ 'orchestration.fields.title': 'Title',
1534
+ 'orchestration.fields.title.placeholder': 'Optional. Defaults to an inferred title from target.',
1535
+ 'orchestration.fields.notes': 'Notes',
1536
+ 'orchestration.fields.notes.placeholder': 'e.g. Do incremental changes only; do not rewrite architecture',
1537
+ 'orchestration.fields.notes.hint': 'Add boundaries, constraints, style rules, or verification requirements.',
1538
+ 'orchestration.fields.followUps': 'Follow-ups (one per line)',
1539
+ 'orchestration.fields.followUps.placeholder': 'e.g.\nAddress new review comments\nAdd regression tests',
1540
+ 'orchestration.fields.concurrency': 'Concurrency',
1541
+ 'orchestration.fields.concurrency.hint': 'Start with 1–2 for complex tasks.',
1542
+ 'orchestration.fields.autoFixRounds': 'Auto-fix',
1543
+ 'orchestration.fields.autoFixRounds.hint': 'Retry a few rounds after failures.',
1544
+ 'orchestration.fields.workflowIds': 'Workflow IDs (one per line)',
1545
+ 'orchestration.fields.workflowIds.placeholder': 'e.g.\ndiagnose-config\nsafe-provider-switch',
1546
+ 'orchestration.fields.workflowIds.hint': 'Required in Workflow mode. {count} available locally.',
1547
+ 'orchestration.workflow.stepCount': '{count} steps',
1548
+ 'orchestration.step3.title': 'Preview, then execute',
1549
+ 'orchestration.step3.subtitle': 'Confirm the plan, then decide to run now or queue.',
1550
+ 'orchestration.actions.planning': 'Planning...',
1551
+ 'orchestration.actions.previewOnly': 'Preview only',
1552
+ 'orchestration.actions.preparing': 'Preparing...',
1553
+ 'orchestration.actions.generatePlan': 'Generate plan',
1554
+ 'orchestration.actions.planAndRun': 'Plan & run',
1555
+ 'orchestration.actions.processing': 'Working...',
1556
+ 'orchestration.actions.queueAndStart': 'Queue & start',
1557
+ 'orchestration.actions.caption': '“Plan & run” refreshes the plan when needed; use “Queue & start” for batch runs.',
1558
+ 'orchestration.stage.title': 'Workspace opens when there is data',
1559
+ 'orchestration.stage.subtitle': 'Write the target first, then preview the plan.',
1560
+ 'orchestration.stage.pill.target': 'Write target',
1561
+ 'orchestration.stage.pill.preview': 'Preview',
1562
+ 'orchestration.stage.pill.run': 'Run or queue',
1563
+ 'orchestration.plan.title': 'Plan preview',
1564
+ 'orchestration.plan.subtitle': 'Confirm nodes, waves, and dependencies.',
1565
+ 'orchestration.plan.summary.nodes': 'Nodes',
1566
+ 'orchestration.plan.summary.waves': 'Waves',
1567
+ 'orchestration.plan.summary.engine': 'Engine',
1568
+ 'orchestration.plan.node.write': 'write',
1569
+ 'orchestration.plan.node.readOnly': 'read-only',
1570
+ 'orchestration.labels.dependencies': 'Dependencies: ',
1571
+ 'orchestration.labels.error': 'Error: ',
1572
+ 'orchestration.workbench.title': 'Workbench',
1573
+ 'orchestration.workbench.subtitle': 'Only expands when there is data.',
1574
+ 'orchestration.queue.start': 'Start queue',
1575
+ 'orchestration.queue.starting': 'Starting...',
1576
+ 'orchestration.workbench.tabs.aria': 'Workbench views',
1577
+ 'orchestration.workbench.tabs.queue': 'Queue {count}',
1578
+ 'orchestration.workbench.tabs.runs': 'Runs {count}',
1579
+ 'orchestration.workbench.tabs.detail': 'Run detail',
1580
+ 'orchestration.queue.empty.title': 'No queued tasks',
1581
+ 'orchestration.queue.empty.subtitle': 'Queue batch tasks first, then start the runner.',
1582
+ 'orchestration.runs.empty.title': 'No runs yet',
1583
+ 'orchestration.runs.empty.subtitle': 'Recent runs will appear after execution.',
1584
+ 'orchestration.detail.refresh': 'Refresh detail',
1585
+ 'orchestration.detail.retry': 'Retry',
1586
+ 'orchestration.detail.retrying': 'Retrying...',
1587
+ 'orchestration.detail.empty.title': 'Select a run to view details',
1588
+ 'orchestration.detail.empty.subtitle': 'This view shows node statuses, summaries, and logs.',
1589
+ 'orchestration.detail.summary.status': 'Status',
1590
+ 'orchestration.detail.summary.duration': 'Duration',
1591
+ 'orchestration.detail.summary.nodes': 'Nodes',
1592
+ 'orchestration.detail.summary.summary': 'Summary',
1593
+ 'orchestration.detail.node.meta': '{id} · attempts {attempts} · auto-fix {autoFix}',
1380
1594
  'skills.localLabel': '{target} / Local skills',
1381
1595
  'skills.counts': '{installed} installed · {importable} importable',
1382
1596
 
@@ -1,220 +1,3 @@
1
- function normalizePromptTemplateEntry(item) {
2
- const safe = item && typeof item === 'object' ? item : {};
3
- const id = typeof safe.id === 'string' ? safe.id.trim() : '';
4
- const name = typeof safe.name === 'string' ? safe.name.trim() : '';
5
- const description = typeof safe.description === 'string' ? safe.description.trim() : '';
6
- const template = typeof safe.template === 'string' ? safe.template : '';
7
- const updatedAt = typeof safe.updatedAt === 'string' ? safe.updatedAt : '';
8
- const createdAt = typeof safe.createdAt === 'string' ? safe.createdAt : updatedAt;
9
- const isBuiltin = safe.isBuiltin === true;
10
- return {
11
- id,
12
- name,
13
- description,
14
- template,
15
- createdAt,
16
- updatedAt,
17
- isBuiltin
18
- };
19
- }
1
+ import { createPluginsComputed } from '../../plugins/prompt-templates/computed.mjs';
20
2
 
21
- const TEMPLATE_PARTS_CACHE = new Map();
22
-
23
- function parseTemplateVariables(templateText) {
24
- const text = typeof templateText === 'string' ? templateText : '';
25
- const vars = new Set();
26
- const re = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
27
- for (;;) {
28
- const match = re.exec(text);
29
- if (!match) break;
30
- const name = String(match[1] || '').trim();
31
- if (name) vars.add(name);
32
- }
33
- return Array.from(vars).sort((a, b) => a.localeCompare(b, 'en-US'));
34
- }
35
-
36
- function parseTemplateParts(templateText) {
37
- const text = typeof templateText === 'string' ? templateText : '';
38
- const parts = [];
39
- const re = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
40
- let lastIndex = 0;
41
- for (;;) {
42
- const match = re.exec(text);
43
- if (!match) break;
44
- const matchIndex = match.index;
45
- if (matchIndex > lastIndex) {
46
- parts.push({ type: 'text', value: text.slice(lastIndex, matchIndex) });
47
- }
48
- const name = String(match[1] || '').trim();
49
- parts.push({ type: 'var', name: name || '' });
50
- lastIndex = matchIndex + match[0].length;
51
- }
52
- if (lastIndex < text.length) {
53
- parts.push({ type: 'text', value: text.slice(lastIndex) });
54
- }
55
- return parts.filter((part) => {
56
- if (!part) return false;
57
- if (part.type === 'text') return typeof part.value === 'string' && part.value.length > 0;
58
- if (part.type === 'var') return typeof part.name === 'string' && part.name.trim().length > 0;
59
- return false;
60
- });
61
- }
62
-
63
- function getCachedTemplateParts(templateKey, templateText) {
64
- const cacheKey = `${templateKey}::${templateText}`;
65
- if (TEMPLATE_PARTS_CACHE.has(cacheKey)) {
66
- return TEMPLATE_PARTS_CACHE.get(cacheKey);
67
- }
68
- const parts = parseTemplateParts(templateText);
69
- TEMPLATE_PARTS_CACHE.set(cacheKey, parts);
70
- // simple cap to avoid unbounded growth
71
- if (TEMPLATE_PARTS_CACHE.size > 64) {
72
- const firstKey = TEMPLATE_PARTS_CACHE.keys().next().value;
73
- TEMPLATE_PARTS_CACHE.delete(firstKey);
74
- }
75
- return parts;
76
- }
77
-
78
- function formatIsoDateLabel(iso) {
79
- if (!iso) return '';
80
- const ms = Date.parse(iso);
81
- if (!Number.isFinite(ms)) return '';
82
- const date = new Date(ms);
83
- const y = String(date.getFullYear());
84
- const m = String(date.getMonth() + 1).padStart(2, '0');
85
- const d = String(date.getDate()).padStart(2, '0');
86
- return `${y}-${m}-${d}`;
87
- }
88
-
89
- function renderTemplate(templateText, values = {}) {
90
- const text = typeof templateText === 'string' ? templateText : '';
91
- const map = values && typeof values === 'object' ? values : {};
92
- return text.replace(/\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g, (_whole, key) => {
93
- const name = String(key || '').trim();
94
- if (!name) return '';
95
- const value = map[name];
96
- return value == null ? '' : String(value);
97
- });
98
- }
99
-
100
- export function createPluginsComputed() {
101
- return {
102
- pluginsCatalog() {
103
- return [
104
- {
105
- id: 'prompt-templates',
106
- title: 'Prompt Templates',
107
- description: 'Standardized, template-driven prompts with variables and copy/export helpers.',
108
- statusLabel: 'standard',
109
- tone: 'configured'
110
- }
111
- ];
112
- },
113
-
114
- promptTemplatesList() {
115
- const list = Array.isArray(this.promptTemplatesListRaw) ? this.promptTemplatesListRaw : [];
116
- return list
117
- .map((item) => normalizePromptTemplateEntry(item))
118
- .filter((item) => item.id && item.name)
119
- .map((item) => {
120
- const vars = parseTemplateVariables(item.template);
121
- const updatedLabel = formatIsoDateLabel(item.updatedAt || item.createdAt);
122
- return {
123
- ...item,
124
- vars,
125
- varCount: vars.length,
126
- updatedLabel: updatedLabel || '—'
127
- };
128
- })
129
- .sort((a, b) => {
130
- const aTime = Date.parse(a.updatedAt || a.createdAt || '') || 0;
131
- const bTime = Date.parse(b.updatedAt || b.createdAt || '') || 0;
132
- if (bTime !== aTime) return bTime - aTime;
133
- return a.name.localeCompare(b.name, 'en-US');
134
- });
135
- },
136
-
137
- filteredPromptTemplates() {
138
- const keyword = typeof this.promptTemplatesKeyword === 'string'
139
- ? this.promptTemplatesKeyword.trim().toLowerCase()
140
- : '';
141
- const list = this.promptTemplatesList;
142
- if (!keyword) return list;
143
- return list.filter((item) => {
144
- return (
145
- item.name.toLowerCase().includes(keyword)
146
- || (item.description && item.description.toLowerCase().includes(keyword))
147
- || item.vars.some((v) => v.toLowerCase().includes(keyword))
148
- );
149
- });
150
- },
151
-
152
- promptTemplateDraft() {
153
- const draft = this.promptTemplateDraftRaw;
154
- if (!draft || typeof draft !== 'object') return null;
155
- const id = typeof draft.id === 'string' ? draft.id : '';
156
- const name = typeof draft.name === 'string' ? draft.name : '';
157
- if (!id && !name) return null;
158
- return normalizePromptTemplateEntry(draft);
159
- },
160
-
161
- promptTemplateVars() {
162
- const draft = this.promptTemplateDraft;
163
- if (!draft) return [];
164
- return parseTemplateVariables(draft.template);
165
- },
166
-
167
- promptTemplateVarValues() {
168
- const values = this.promptTemplateVarValuesRaw;
169
- return values && typeof values === 'object' ? values : {};
170
- },
171
-
172
- renderedPrompt() {
173
- const draft = this.promptTemplateDraft;
174
- if (!draft) return '';
175
- return renderTemplate(draft.template, this.promptTemplateVarValues);
176
- },
177
-
178
- promptComposerVarValues() {
179
- const values = this.promptComposerVarValuesRaw;
180
- return values && typeof values === 'object' ? values : {};
181
- },
182
-
183
- promptComposerActiveTemplate() {
184
- const id = typeof this.promptComposerSelectedTemplateId === 'string'
185
- ? this.promptComposerSelectedTemplateId.trim()
186
- : '';
187
- if (!id) return null;
188
- const list = this.promptTemplatesList;
189
- return list.find((item) => item.id === id) || null;
190
- },
191
-
192
- promptComposerParts() {
193
- const tpl = this.promptComposerActiveTemplate;
194
- if (!tpl) return [];
195
- const key = tpl.id || tpl.name || 'template';
196
- return getCachedTemplateParts(key, tpl.template);
197
- },
198
-
199
- promptComposerRendered() {
200
- const tpl = this.promptComposerActiveTemplate;
201
- if (!tpl) return '';
202
- return renderTemplate(tpl.template, this.promptComposerVarValues);
203
- },
204
-
205
- promptComposerPickerList() {
206
- const keyword = typeof this.promptComposerPickerKeyword === 'string'
207
- ? this.promptComposerPickerKeyword.trim().toLowerCase()
208
- : '';
209
- const list = this.promptTemplatesList;
210
- if (!keyword) return list;
211
- return list.filter((item) => {
212
- return (
213
- item.name.toLowerCase().includes(keyword)
214
- || (item.description && item.description.toLowerCase().includes(keyword))
215
- || item.vars.some((v) => v.toLowerCase().includes(keyword))
216
- );
217
- });
218
- }
219
- };
220
- }
3
+ export { createPluginsComputed };