skill-chef 0.1.0

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.
Files changed (81) hide show
  1. package/dist/cli/cmd_read.d.ts +3 -0
  2. package/dist/cli/cmd_read.d.ts.map +1 -0
  3. package/dist/cli/cmd_read.js +434 -0
  4. package/dist/cli/cmd_read.js.map +1 -0
  5. package/dist/fetch/db.d.ts +112 -0
  6. package/dist/fetch/db.d.ts.map +1 -0
  7. package/dist/fetch/db.js +463 -0
  8. package/dist/fetch/db.js.map +1 -0
  9. package/dist/fetch/errors.d.ts +13 -0
  10. package/dist/fetch/errors.d.ts.map +1 -0
  11. package/dist/fetch/errors.js +18 -0
  12. package/dist/fetch/errors.js.map +1 -0
  13. package/dist/fetch/index.d.ts +6 -0
  14. package/dist/fetch/index.d.ts.map +1 -0
  15. package/dist/fetch/index.js +4 -0
  16. package/dist/fetch/index.js.map +1 -0
  17. package/dist/fetch/paths.d.ts +6 -0
  18. package/dist/fetch/paths.d.ts.map +1 -0
  19. package/dist/fetch/paths.js +31 -0
  20. package/dist/fetch/paths.js.map +1 -0
  21. package/dist/help/index.d.ts +2 -0
  22. package/dist/help/index.d.ts.map +1 -0
  23. package/dist/help/index.js +2 -0
  24. package/dist/help/index.js.map +1 -0
  25. package/dist/help/lookup.d.ts +11 -0
  26. package/dist/help/lookup.d.ts.map +1 -0
  27. package/dist/help/lookup.js +49 -0
  28. package/dist/help/lookup.js.map +1 -0
  29. package/dist/index.d.ts +5 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +5 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/policy/category.d.ts +10 -0
  34. package/dist/policy/category.d.ts.map +1 -0
  35. package/dist/policy/category.js +45 -0
  36. package/dist/policy/category.js.map +1 -0
  37. package/dist/policy/index.d.ts +6 -0
  38. package/dist/policy/index.d.ts.map +1 -0
  39. package/dist/policy/index.js +4 -0
  40. package/dist/policy/index.js.map +1 -0
  41. package/dist/policy/recipe.d.ts +10 -0
  42. package/dist/policy/recipe.d.ts.map +1 -0
  43. package/dist/policy/recipe.js +52 -0
  44. package/dist/policy/recipe.js.map +1 -0
  45. package/dist/policy/wakewords.d.ts +14 -0
  46. package/dist/policy/wakewords.d.ts.map +1 -0
  47. package/dist/policy/wakewords.js +67 -0
  48. package/dist/policy/wakewords.js.map +1 -0
  49. package/dist/render/envelope.d.ts +6 -0
  50. package/dist/render/envelope.d.ts.map +1 -0
  51. package/dist/render/envelope.js +48 -0
  52. package/dist/render/envelope.js.map +1 -0
  53. package/dist/render/errors.d.ts +5 -0
  54. package/dist/render/errors.d.ts.map +1 -0
  55. package/dist/render/errors.js +9 -0
  56. package/dist/render/errors.js.map +1 -0
  57. package/dist/render/html.d.ts +13 -0
  58. package/dist/render/html.d.ts.map +1 -0
  59. package/dist/render/html.js +75 -0
  60. package/dist/render/html.js.map +1 -0
  61. package/dist/render/index.d.ts +8 -0
  62. package/dist/render/index.d.ts.map +1 -0
  63. package/dist/render/index.js +6 -0
  64. package/dist/render/index.js.map +1 -0
  65. package/dist/render/templates.d.ts +5 -0
  66. package/dist/render/templates.d.ts.map +1 -0
  67. package/dist/render/templates.js +42 -0
  68. package/dist/render/templates.js.map +1 -0
  69. package/dist/render/views.d.ts +85 -0
  70. package/dist/render/views.d.ts.map +1 -0
  71. package/dist/render/views.js +93 -0
  72. package/dist/render/views.js.map +1 -0
  73. package/package.json +32 -0
  74. package/templates/cooking_run.html +16 -0
  75. package/templates/help.html +16 -0
  76. package/templates/history_query.html +16 -0
  77. package/templates/history_record.html +16 -0
  78. package/templates/recipe_search.html +16 -0
  79. package/templates/recipe_view.html +16 -0
  80. package/templates/recipe_write.html +16 -0
  81. package/templates/shopping_query.html +16 -0
@@ -0,0 +1,2 @@
1
+ export { buildHelpLookup, lookupWake } from './lookup.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/help/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAgB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { type ChefKey } from '../policy/index.js';
2
+ export interface HelpHit {
3
+ phrase: string;
4
+ key: ChefKey;
5
+ shape: string;
6
+ cli: string;
7
+ desc: string;
8
+ }
9
+ export declare function buildHelpLookup(): HelpHit[];
10
+ export declare function lookupWake(hits: HelpHit[], word: string): HelpHit[];
11
+ //# sourceMappingURL=lookup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../../src/help/lookup.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG9D,MAAM,WAAW,OAAO;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;CAAE;AAuBpG,wBAAgB,eAAe,IAAI,OAAO,EAAE,CAQ3C;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAWnE"}
@@ -0,0 +1,49 @@
1
+ // HELP 速查:WAKE_TABLE 唯一上游;短语→key/cli/一句话。构建期注入 SKILL.md(scripts/build-help.mjs)。
2
+ import { WAKE_TABLE } from '../policy/index.js';
3
+ import { CHEF_KEY_SHAPES } from '../render/index.js';
4
+ function exampleParams(e) {
5
+ const p = { ...(e.preset || {}) };
6
+ for (const n of e.needs || []) {
7
+ if (p[n] === undefined)
8
+ p[n] = n === 'id' ? 1 : n === 'recipe_id' ? 1 : n === 'name' ? '宫保虾球' : n === 'nameOrId' ? '宫保虾球' : n === 'names' ? ['宫保虾球', '鱼香肉丝'] : n === 'q' ? '排骨' : n === 'filter' ? '川菜' : n === 'action' ? '大火翻炒' : n === 'rating' ? 5 : n === 'feedback' ? '好吃' : n === 'date' ? '2026-09-06' : n === 'servings' ? 2 : n === 'quantity' ? 2 : n === 'unit' ? '克' : n === 'patch' ? { servings: 3 } : '<值>';
9
+ }
10
+ const keys = Object.keys(p);
11
+ return keys.length ? ' --params \'' + JSON.stringify(p) + '\'' : '';
12
+ }
13
+ const DESCS = {
14
+ 'chef.recipe.view': '看单菜全貌(基本+食材+步骤+历史统计,须 nameOrId)',
15
+ 'chef.recipe.search': '搜菜/按条件筛菜/看全部(q 或 cuisine/season/method/flavor/tag/meal/cookware/difficulty/status/maxTime/filter,filter=菜系别名)',
16
+ 'chef.recipe.write': '新增/改菜/废弃/加食材/加步骤(op 分流,写走回执)',
17
+ 'chef.cooking.run': '开做(步骤内联食材+份数放大说明,须 nameOrId)',
18
+ 'chef.shopping.query': '跨菜合并采购清单(names 数组+servings+excludeOptional)',
19
+ 'chef.history.record': '记一次做菜(name+rating/feedback/date,写走回执)',
20
+ 'chef.history.query': '查做菜时间线/统计/质检/备份(kind 分流,统一 list)',
21
+ 'chef.help.lookup': '能力速查 HELP 现找',
22
+ };
23
+ // 全量速查表(WAKE_TABLE 37 短语:help.lookup4 + recipe.view7 + recipe.search8 + recipe.write4 + cooking.run4 + shopping.query4 + history.record3 + history.query3)。
24
+ export function buildHelpLookup() {
25
+ return WAKE_TABLE.map((e) => ({
26
+ phrase: e.phrase,
27
+ key: e.key,
28
+ shape: CHEF_KEY_SHAPES[e.key] || '??',
29
+ cli: 'chef-cmd-read ' + e.key + exampleParams(e),
30
+ desc: DESCS[e.key] || '',
31
+ }));
32
+ }
33
+ export function lookupWake(hits, word) {
34
+ const exact = hits.filter((h) => word.includes(h.phrase));
35
+ if (exact.length)
36
+ return exact;
37
+ const q = word || '';
38
+ if (['搜', '找', '查', '寻'].some((c) => q.includes(c))) {
39
+ const rs = hits.filter((h) => h.key.includes('recipe.search'));
40
+ if (rs.length)
41
+ return rs;
42
+ }
43
+ if (['做菜', '做饭', '下厨'].some((c) => q.includes(c)))
44
+ return hits.filter((h) => h.key.includes('cooking.run'));
45
+ if (['清单', '买菜'].some((c) => q.includes(c)))
46
+ return hits.filter((h) => h.key.includes('shopping.query'));
47
+ return [];
48
+ }
49
+ //# sourceMappingURL=lookup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../src/help/lookup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export * from './fetch/index.js';
2
+ export * from './policy/index.js';
3
+ export * from './render/index.js';
4
+ export * from './help/index.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from './fetch/index.js';
2
+ export * from './policy/index.js';
3
+ export * from './render/index.js';
4
+ export * from './help/index.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,10 @@
1
+ export declare const DIFFICULTIES: readonly ["快手菜", "简单", "中等", "困难", "大师"];
2
+ export declare const STATUSES: readonly ["未做", "已做", "熟练", "已废弃"];
3
+ export declare const HEATS: readonly ["微火", "小火", "中火", "大火", "猛火"];
4
+ export declare const INGREDIENT_CATEGORIES: readonly ["肉类", "海鲜", "蛋类", "蔬菜", "葱姜蒜", "香草", "调料", "豆制品", "主食", "干货", "其他"];
5
+ export declare function validateDifficulty(raw: unknown): string;
6
+ export declare function validateStatus(raw: unknown): string;
7
+ export declare function validateHeat(raw: unknown): string;
8
+ export declare function validateCategory(raw: unknown): string;
9
+ export declare function validateRating(raw: unknown): number;
10
+ //# sourceMappingURL=category.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"category.d.ts","sourceRoot":"","sources":["../../src/policy/category.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY,0CAA2C,CAAC;AACrE,eAAO,MAAM,QAAQ,oCAAqC,CAAC;AAC3D,eAAO,MAAM,KAAK,yCAA0C,CAAC;AAC7D,eAAO,MAAM,qBAAqB,+EAAgF,CAAC;AAEnH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAKvD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAKnD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAKjD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAWrD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAKnD"}
@@ -0,0 +1,45 @@
1
+ // 口径层·枚举校验(遗产 references/enums.py 原文):难度/状态/火候/食材分类/评分。
2
+ // 错值一律抛 ChefPolicyError,永不返空冒充。
3
+ import { ChefPolicyError } from '../fetch/errors.js';
4
+ export const DIFFICULTIES = ['快手菜', '简单', '中等', '困难', '大师'];
5
+ export const STATUSES = ['未做', '已做', '熟练', '已废弃'];
6
+ export const HEATS = ['微火', '小火', '中火', '大火', '猛火'];
7
+ export const INGREDIENT_CATEGORIES = ['肉类', '海鲜', '蛋类', '蔬菜', '葱姜蒜', '香草', '调料', '豆制品', '主食', '干货', '其他'];
8
+ export function validateDifficulty(raw) {
9
+ if (typeof raw !== 'string' || !raw.trim() || !DIFFICULTIES.includes(raw.trim())) {
10
+ throw new ChefPolicyError('POLICY_BAD_DIFFICULTY', '难度非法(期望 快手菜/简单/中等/困难/大师):' + JSON.stringify(raw));
11
+ }
12
+ return raw.trim();
13
+ }
14
+ export function validateStatus(raw) {
15
+ if (typeof raw !== 'string' || !raw.trim() || !STATUSES.includes(raw.trim())) {
16
+ throw new ChefPolicyError('POLICY_BAD_STATUS', '状态非法(期望 未做/已做/熟练/已废弃):' + JSON.stringify(raw));
17
+ }
18
+ return raw.trim();
19
+ }
20
+ export function validateHeat(raw) {
21
+ if (typeof raw !== 'string' || !raw.trim() || !HEATS.includes(raw.trim())) {
22
+ throw new ChefPolicyError('POLICY_BAD_HEAT', '火候非法(期望 微火/小火/中火/大火/猛火):' + JSON.stringify(raw));
23
+ }
24
+ return raw.trim();
25
+ }
26
+ export function validateCategory(raw) {
27
+ if (typeof raw !== 'string' || !raw.trim()) {
28
+ throw new ChefPolicyError('POLICY_BAD_CATEGORY', '食材分类非法(期望 肉类/海鲜/蛋类/蔬菜/葱姜蒜/香草/调料/豆制品/主食/干货/其他):' + JSON.stringify(raw));
29
+ }
30
+ const t = raw.trim();
31
+ // 别名归一:水产为海鲜口语别名,统一归一到海鲜(enum 以海鲜为准,遗产 references/enums.py 原文)。
32
+ if (t === '水产' || t === '海鲜')
33
+ return '海鲜';
34
+ if (!INGREDIENT_CATEGORIES.includes(t)) {
35
+ throw new ChefPolicyError('POLICY_BAD_CATEGORY', '食材分类非法(期望 肉类/海鲜/蛋类/蔬菜/葱姜蒜/香草/调料/豆制品/主食/干货/其他):' + JSON.stringify(raw));
36
+ }
37
+ return t;
38
+ }
39
+ export function validateRating(raw) {
40
+ if (typeof raw !== 'number' || !Number.isFinite(raw) || raw < 0 || raw > 5) {
41
+ throw new ChefPolicyError('POLICY_BAD_RATING', '评分非法(期望 0-5 数字,允许小数):' + JSON.stringify(raw));
42
+ }
43
+ return raw;
44
+ }
45
+ //# sourceMappingURL=category.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"category.js","sourceRoot":"","sources":["../../src/policy/category.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { DIFFICULTIES, STATUSES, HEATS, INGREDIENT_CATEGORIES, validateDifficulty, validateStatus, validateHeat, validateCategory, validateRating } from './category.js';
2
+ export { parseWriteOp, parseRecipeOp, parseHistoryKind, needName, needNameOrId, needNames } from './recipe.js';
3
+ export type { WriteOp, RecipeOp, HistoryKind } from './recipe.js';
4
+ export { routeWakeword, WAKE_TABLE } from './wakewords.js';
5
+ export type { ChefKey, WakeRoute, WakeEntry } from './wakewords.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,cAAc,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACzK,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC/G,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC3D,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { DIFFICULTIES, STATUSES, HEATS, INGREDIENT_CATEGORIES, validateDifficulty, validateStatus, validateHeat, validateCategory, validateRating } from './category.js';
2
+ export { parseWriteOp, parseRecipeOp, parseHistoryKind, needName, needNameOrId, needNames } from './recipe.js';
3
+ export { routeWakeword, WAKE_TABLE } from './wakewords.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,cAAc,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACzK,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE/G,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,10 @@
1
+ export type WriteOp = 'add' | 'update' | 'deprecate';
2
+ export declare function parseWriteOp(params: Record<string, unknown>): WriteOp;
3
+ export declare function needName(params: Record<string, unknown>, field?: string): string;
4
+ export declare function needNameOrId(params: Record<string, unknown>): string;
5
+ export type RecipeOp = 'add' | 'update' | 'discard' | 'add-ingredient' | 'add-step';
6
+ export declare function parseRecipeOp(params: Record<string, unknown>): RecipeOp;
7
+ export type HistoryKind = 'timeline' | 'stats' | 'quality' | 'backup';
8
+ export declare function parseHistoryKind(params: Record<string, unknown>): HistoryKind;
9
+ export declare function needNames(params: Record<string, unknown>, field?: string): string[];
10
+ //# sourceMappingURL=recipe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe.d.ts","sourceRoot":"","sources":["../../src/policy/recipe.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,WAAW,CAAC;AAErD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAIrE;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,SAAS,GAAG,MAAM,CAIhF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAOpE;AAED,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,gBAAgB,GAAG,UAAU,CAAC;AAEpF,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ,CAIvE;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEtE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,CAM7E;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,SAAU,GAAG,MAAM,EAAE,CAMpF"}
@@ -0,0 +1,52 @@
1
+ // 口径层·写参薄封装供 cli 用(老家 recipe_manager/history/shopping 对应 params 口径)。
2
+ // cli 若已定接口则保持兼容:仅做缺槽位与 op 分流,不做重校验。
3
+ import { ChefPolicyError } from '../fetch/errors.js';
4
+ export function parseWriteOp(params) {
5
+ const op = params.op === undefined ? 'add' : params.op;
6
+ if (op === 'add' || op === 'update' || op === 'deprecate')
7
+ return op;
8
+ throw new ChefPolicyError('POLICY_BAD_INPUT', 'op 非法(期望 add/update/deprecate):' + JSON.stringify(op));
9
+ }
10
+ export function needName(params, field = 'name') {
11
+ const v = params[field];
12
+ if (typeof v !== 'string' || !v.trim())
13
+ throw new ChefPolicyError('POLICY_MISSING_SLOT', '缺槽位 ' + field);
14
+ return v.trim();
15
+ }
16
+ export function needNameOrId(params) {
17
+ const name = params.name;
18
+ if (typeof name === 'string' && name.trim())
19
+ return name.trim();
20
+ const id = params.id;
21
+ if (typeof id === 'string' && id.trim())
22
+ return id.trim();
23
+ if (typeof id === 'number' && Number.isFinite(id))
24
+ return String(id);
25
+ throw new ChefPolicyError('POLICY_MISSING_SLOT', '缺槽位 name/id(查看须给菜名或 id)');
26
+ }
27
+ export function parseRecipeOp(params) {
28
+ const op = params.op === undefined ? 'add' : params.op;
29
+ if (op === 'add' || op === 'update' || op === 'discard' || op === 'add-ingredient' || op === 'add-step')
30
+ return op;
31
+ throw new ChefPolicyError('POLICY_BAD_INPUT', 'op 非法(期望 add/update/discard/add-ingredient/add-step):' + JSON.stringify(op));
32
+ }
33
+ export function parseHistoryKind(params) {
34
+ const kind = params.kind === undefined ? undefined : params.kind;
35
+ if (kind === 'timeline' || kind === 'stats' || kind === 'quality' || kind === 'backup')
36
+ return kind;
37
+ if (kind !== undefined)
38
+ throw new ChefPolicyError('POLICY_BAD_INPUT', 'kind 非法(期望 timeline/stats/quality/backup):' + JSON.stringify(kind));
39
+ if (typeof params.name === 'string' && params.name.trim())
40
+ return 'timeline';
41
+ return 'stats';
42
+ }
43
+ export function needNames(params, field = 'names') {
44
+ const v = params[field];
45
+ if (!Array.isArray(v) || v.length === 0)
46
+ throw new ChefPolicyError('POLICY_MISSING_SLOT', '缺槽位 ' + field + '(期望非空数组)');
47
+ const out = v.map((x) => (typeof x === 'string' ? x.trim() : ''));
48
+ if (out.some((x) => !x))
49
+ throw new ChefPolicyError('POLICY_BAD_INPUT', field + ' 含空串');
50
+ return out;
51
+ }
52
+ //# sourceMappingURL=recipe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe.js","sourceRoot":"","sources":["../../src/policy/recipe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export type ChefKey = 'chef.recipe.view' | 'chef.recipe.search' | 'chef.recipe.write' | 'chef.cooking.run' | 'chef.shopping.query' | 'chef.history.record' | 'chef.history.query' | 'chef.help.lookup';
2
+ export interface WakeRoute {
3
+ key: ChefKey;
4
+ params: Record<string, unknown>;
5
+ }
6
+ export interface WakeEntry {
7
+ phrase: string;
8
+ key: ChefKey;
9
+ needs?: string[];
10
+ preset?: Record<string, unknown>;
11
+ }
12
+ export declare const WAKE_TABLE: WakeEntry[];
13
+ export declare function routeWakeword(text: string, ctx?: Record<string, unknown>): WakeRoute;
14
+ //# sourceMappingURL=wakewords.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakewords.d.ts","sourceRoot":"","sources":["../../src/policy/wakewords.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GACf,kBAAkB,GAAG,oBAAoB,GAAG,mBAAmB,GAAG,kBAAkB,GACpF,qBAAqB,GAAG,qBAAqB,GAAG,oBAAoB,GAAG,kBAAkB,CAAC;AAE9F,MAAM,WAAW,SAAS;IAAG,GAAG,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAAE;AAC7E,MAAM,WAAW,SAAS;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAAE;AAEhH,eAAO,MAAM,UAAU,EAAE,SAAS,EAsCjC,CAAC;AAIF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,SAAS,CAmBxF"}
@@ -0,0 +1,67 @@
1
+ // 口径层·唤醒词路由:自然语言 → 8 联动 key;最长匹配;无命中/缺槽位 throw。
2
+ // HELP 速查唯一上游;改这里,HELP 构建期跟进。37 条 = help4 + view7 + search8 (#43 F1 看菜/看菜谱→view) + write4 + cooking4 + shopping4 + record3 + query3。
3
+ import { ChefPolicyError } from '../fetch/errors.js';
4
+ export const WAKE_TABLE = [
5
+ { phrase: '私家大厨HELP', key: 'chef.help.lookup' },
6
+ { phrase: '菜谱HELP', key: 'chef.help.lookup' },
7
+ { phrase: '查帮助', key: 'chef.help.lookup' },
8
+ { phrase: '能做什么', key: 'chef.help.lookup' },
9
+ { phrase: '查看食谱', key: 'chef.recipe.view', needs: ['name'] },
10
+ { phrase: '查看食材', key: 'chef.recipe.view', needs: ['name'] },
11
+ { phrase: '查看步骤', key: 'chef.recipe.view', needs: ['name'] },
12
+ { phrase: '查看营养', key: 'chef.recipe.view', needs: ['name'] },
13
+ { phrase: '查看背景', key: 'chef.recipe.view', needs: ['name'] },
14
+ { phrase: '看菜谱', key: 'chef.recipe.view', needs: ['name'] },
15
+ { phrase: '看菜', key: 'chef.recipe.view', needs: ['name'] },
16
+ { phrase: '查看全部', key: 'chef.recipe.search', preset: { kind: 'all' } },
17
+ { phrase: '搜索食谱', key: 'chef.recipe.search', needs: ['q'] },
18
+ { phrase: '搜菜', key: 'chef.recipe.search', needs: ['q'] },
19
+ { phrase: '查食材', key: 'chef.recipe.search', needs: ['q'] },
20
+ { phrase: '筛选菜系', key: 'chef.recipe.search', needs: ['filter'] },
21
+ { phrase: '筛选食材', key: 'chef.recipe.search', needs: ['filter'] },
22
+ { phrase: '筛选口味', key: 'chef.recipe.search', needs: ['filter'] },
23
+ { phrase: '筛选季节', key: 'chef.recipe.search', needs: ['filter'] },
24
+ { phrase: '录入食谱', key: 'chef.recipe.write', needs: ['name'], preset: { op: 'add' } },
25
+ { phrase: '修改食谱', key: 'chef.recipe.write', needs: ['name'], preset: { op: 'update' } },
26
+ { phrase: '废弃食谱', key: 'chef.recipe.write', needs: ['name'], preset: { op: 'deprecate' } },
27
+ { phrase: '加菜', key: 'chef.recipe.write', needs: ['name'], preset: { op: 'add' } },
28
+ { phrase: '做菜模式', key: 'chef.cooking.run', needs: ['name'] },
29
+ { phrase: '开始做菜', key: 'chef.cooking.run', needs: ['name'] },
30
+ { phrase: '继续做菜', key: 'chef.cooking.run', needs: ['name'] },
31
+ { phrase: '完成做菜', key: 'chef.cooking.run', needs: ['name'] },
32
+ { phrase: '生成清单', key: 'chef.shopping.query', needs: ['names'] },
33
+ { phrase: '排除可选', key: 'chef.shopping.query', needs: ['names'] },
34
+ { phrase: '查清单', key: 'chef.shopping.query', needs: ['names'] },
35
+ { phrase: '清空清单', key: 'chef.shopping.query', needs: ['names'] },
36
+ { phrase: '记录做菜', key: 'chef.history.record', needs: ['name'] },
37
+ { phrase: '补录做菜', key: 'chef.history.record', needs: ['name'] },
38
+ { phrase: '改评分', key: 'chef.history.record', needs: ['name'] },
39
+ { phrase: '查看历史', key: 'chef.history.query', needs: ['name'] },
40
+ { phrase: '查看统计', key: 'chef.history.query', preset: { kind: 'stats' } },
41
+ { phrase: '体检', key: 'chef.history.query', preset: { kind: 'quality' } },
42
+ ];
43
+ const SORTED = [...WAKE_TABLE].sort((a, b) => b.phrase.length - a.phrase.length);
44
+ export function routeWakeword(text, ctx = {}) {
45
+ if (typeof text !== 'string' || text.length === 0)
46
+ throw new ChefPolicyError('POLICY_NO_MATCH', '唤醒词为空');
47
+ const hit = SORTED.find((e) => text.includes(e.phrase));
48
+ if (!hit)
49
+ throw new ChefPolicyError('POLICY_NO_MATCH', '无命中唤醒词:' + text);
50
+ for (const s of hit.needs || []) {
51
+ const v = ctx[s];
52
+ if (v === undefined || v === null || v === '') {
53
+ throw new ChefPolicyError('POLICY_MISSING_SLOT', '缺槽位 ' + s + ':' + hit.phrase);
54
+ }
55
+ }
56
+ const params = { ...(hit.preset || {}) };
57
+ for (const k of hit.needs || [])
58
+ params[k] = ctx[k];
59
+ if (hit.key === 'chef.history.record' && ctx.rating !== undefined && params.rating === undefined) {
60
+ params.rating = ctx.rating;
61
+ }
62
+ if (hit.key === 'chef.history.record' && ctx.feedback !== undefined && params.feedback === undefined) {
63
+ params.feedback = ctx.feedback;
64
+ }
65
+ return { key: hit.key, params };
66
+ }
67
+ //# sourceMappingURL=wakewords.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wakewords.js","sourceRoot":"","sources":["../../src/policy/wakewords.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type Envelope, type EnvelopeShape } from 'base-link-core';
2
+ export declare const CHEF_KEY_SHAPES: Record<string, EnvelopeShape>;
3
+ export declare function chefShapeFor(key: string): EnvelopeShape;
4
+ export declare function buildChefEnvelope(key: string, data: unknown): Envelope;
5
+ export declare function parseChefEnvelope(input: unknown): Envelope;
6
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/render/envelope.ts"],"names":[],"mappings":"AACA,OAAO,EAAmD,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CASzD,CAAC;AAEF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAIvD;AAGD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,CAatE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAG1D"}
@@ -0,0 +1,48 @@
1
+ // 渲染层·envelope:8 联动 key×shape 映射;key 字符串后续票落表时冻结,此处只做形状分配与全字段校验。
2
+ import { createEnvelope, parseEnvelope, parseRegistryKey } from 'base-link-core';
3
+ import { ChefRenderError } from './errors.js';
4
+ export const CHEF_KEY_SHAPES = {
5
+ 'chef.recipe.view': 'detail',
6
+ 'chef.recipe.search': 'list',
7
+ 'chef.recipe.write': 'receipt',
8
+ 'chef.cooking.run': 'list',
9
+ 'chef.shopping.query': 'list',
10
+ 'chef.history.record': 'receipt',
11
+ 'chef.history.query': 'list',
12
+ 'chef.help.lookup': 'list',
13
+ };
14
+ export function chefShapeFor(key) {
15
+ const s = CHEF_KEY_SHAPES[key];
16
+ if (!s)
17
+ throw new ChefRenderError('CHEF_UNKNOWN_KEY', '未知联动 key:' + key);
18
+ return s;
19
+ }
20
+ // 建 envelope:key 先过命名空间,再按分配形状做全字段校验;错形状载荷即 throw。
21
+ export function buildChefEnvelope(key, data) {
22
+ let parsedKey = '';
23
+ try {
24
+ parsedKey = parseRegistryKey(key).key;
25
+ }
26
+ catch (e) {
27
+ throw new ChefRenderError('CHEF_UNKNOWN_KEY', '非法 key:' + e.message);
28
+ }
29
+ const shape = chefShapeFor(parsedKey);
30
+ if (typeof data !== 'object' || data === null || Array.isArray(data)) {
31
+ throw new ChefRenderError('CHEF_BAD_PAYLOAD', '载荷须为对象:' + parsedKey);
32
+ }
33
+ try {
34
+ return createEnvelope({ skill: 'chef', shape, key: parsedKey, data: data });
35
+ }
36
+ catch (e) {
37
+ throw new ChefRenderError('CHEF_SHAPE_MISMATCH', shape + ' 全字段未过:' + e.message);
38
+ }
39
+ }
40
+ export function parseChefEnvelope(input) {
41
+ try {
42
+ return parseEnvelope(input);
43
+ }
44
+ catch (e) {
45
+ throw new ChefRenderError('CHEF_BAD_PAYLOAD', 'envelope 非法:' + e.message);
46
+ }
47
+ }
48
+ //# sourceMappingURL=envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../src/render/envelope.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,gBAAgB,EAAqC,MAAM,gBAAgB,CAAC;AACpH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,CAAC,MAAM,eAAe,GAAkC;IAC5D,kBAAkB,EAAE,QAAQ;IAC5B,oBAAoB,EAAE,MAAM;IAC5B,mBAAmB,EAAE,SAAS;IAC9B,kBAAkB,EAAE,MAAM;IAC1B,qBAAqB,EAAE,MAAM;IAC7B,qBAAqB,EAAE,SAAS;IAChC,oBAAoB,EAAE,MAAM;IAC5B,kBAAkB,EAAE,MAAM;CAC3B,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,MAAM,CAAC,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,eAAe,CAAC,kBAAkB,EAAE,WAAW,GAAG,GAAG,CAAC,CAAC;IACzE,OAAO,CAAC,CAAC;AACX,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,iBAAiB,CAAC,GAAW,EAAE,IAAa;IAC1D,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,CAAC;QAAC,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;IAAC,CAAC;IAC9C,OAAO,CAAC,EAAE,CAAC;QAAC,MAAM,IAAI,eAAe,CAAC,kBAAkB,EAAE,SAAS,GAAI,CAAW,CAAC,OAAO,CAAC,CAAC;IAAC,CAAC;IAC9F,MAAM,KAAK,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IACtC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,eAAe,CAAC,kBAAkB,EAAE,SAAS,GAAG,SAAS,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,CAAC;QACH,OAAO,cAAc,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,IAAa,EAAE,CAAC,CAAC;IACvF,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,eAAe,CAAC,qBAAqB,EAAE,KAAK,GAAG,SAAS,GAAI,CAAW,CAAC,OAAO,CAAC,CAAC;IAC7F,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,IAAI,CAAC;QAAC,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;IAAC,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC;QAAC,MAAM,IAAI,eAAe,CAAC,kBAAkB,EAAE,cAAc,GAAI,CAAW,CAAC,OAAO,CAAC,CAAC;IAAC,CAAC;AACrG,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare class ChefRenderError extends Error {
2
+ readonly code: 'CHEF_UNKNOWN_KEY' | 'CHEF_BAD_PAYLOAD' | 'CHEF_SHAPE_MISMATCH' | 'CHEF_TEMPLATE_MISSING' | 'CHEF_MARKER_INVALID' | 'CHEF_HTML_TOO_LARGE';
3
+ constructor(code: ChefRenderError['code'], message: string);
4
+ }
5
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/render/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EACT,kBAAkB,GAAG,kBAAkB,GAAG,qBAAqB,GAC/D,uBAAuB,GAAG,qBAAqB,GAAG,qBAAqB,CAAC;gBAChE,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM;CAK3D"}
@@ -0,0 +1,9 @@
1
+ export class ChefRenderError extends Error {
2
+ code;
3
+ constructor(code, message) {
4
+ super(message);
5
+ this.name = 'ChefRenderError';
6
+ this.code = code;
7
+ }
8
+ }
9
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/render/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAE+D;IAC5E,YAAY,IAA6B,EAAE,OAAe;QACxD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ import type { Envelope } from 'base-link-core';
2
+ export declare const CHEF_HTML_MAX_BYTES: number;
3
+ export declare function escapeHtml(s: string): string;
4
+ export declare function renderEnvelopeHtml(env: Envelope): string;
5
+ export declare function estimateBytes(html: string): number;
6
+ export declare function assertHtmlSize(html: string, max?: number): void;
7
+ export declare const SHARED_CSS_MARKER = "<!--SHARED-CSS-->";
8
+ export declare const SHARED_HELPERS_MARKER = "<!--SHARED-HELPERS-->";
9
+ export declare const CONTENT_MARKER = "<!--CONTENT-->";
10
+ export declare const SHARED_CSS = ".page{font-family:system-ui,sans-serif;max-width:720px;margin:0 auto;padding:12px}.item{border:1px solid #ddd;border-radius:8px;padding:8px;margin:8px 0}.item-head{display:flex;gap:8px;align-items:center}.badge{background:#eee;border-radius:4px;padding:0 6px}.amt{font-weight:700}.receipt{background:#f0fff0;border:1px solid #090;border-radius:8px;padding:12px}.stat{display:flex;gap:8px}.analysis{white-space:pre-wrap}.hm-empty{color:#888}";
11
+ export declare const SHARED_HELPERS = "<script>function copyItem(id){var e=document.getElementById(id);if(e&&navigator.clipboard){navigator.clipboard.writeText(e.innerText);}}</script>";
12
+ export declare function fillTemplate(template: string, contentHtml: string): string;
13
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/render/html.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C,eAAO,MAAM,mBAAmB,QAAa,CAAC;AAE9C,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5C;AAgCD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAexD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAA4C;AAG/F,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAsB,GAAG,IAAI,CAI5E;AAGD,eAAO,MAAM,iBAAiB,sBAAsB,CAAC;AACrD,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAC7D,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAE/C,eAAO,MAAM,UAAU,6bAA6b,CAAC;AACrd,eAAO,MAAM,cAAc,sJAAsJ,CAAC;AAElL,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAU1E"}
@@ -0,0 +1,75 @@
1
+ import { ChefRenderError } from './errors.js';
2
+ export const CHEF_HTML_MAX_BYTES = 256 * 1024;
3
+ export function escapeHtml(s) {
4
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
5
+ }
6
+ function titleOf(n) {
7
+ for (const k of ['name', 'action', 'message', 'summary', 'title']) {
8
+ const v = n[k];
9
+ if (typeof v === 'string' && v)
10
+ return v;
11
+ }
12
+ const id = n.id;
13
+ return id !== undefined ? '条目 ' + String(id) : '条目';
14
+ }
15
+ function metaOf(n) {
16
+ const parts = [];
17
+ for (const k of ['id', 'sequence', 'cook_date', 'rating', 'difficulty', 'status', 'quantity', 'unit']) {
18
+ const v = n[k];
19
+ if (v !== undefined && v !== null && v !== '')
20
+ parts.push(k + '=' + String(v));
21
+ }
22
+ return parts.join(' ');
23
+ }
24
+ function chefItemHtml(n) {
25
+ const title = escapeHtml(titleOf(n));
26
+ const meta = escapeHtml(metaOf(n));
27
+ return '<div class="item"><div class="item-head"><span class="badge">' + title + '</span><span class="time">' + meta + '</span></div></div>';
28
+ }
29
+ function listHtml(items) {
30
+ if (!items.length)
31
+ return '<div class="hm-empty">暂无记录</div>';
32
+ return items.map((x) => chefItemHtml(x)).join('');
33
+ }
34
+ // 按形状渲染 envelope 为 section 页;未知形状 throw 不返空页。
35
+ export function renderEnvelopeHtml(env) {
36
+ const head = '<section data-skill="chef" data-shape="' + env.shape + '" data-key="' + escapeHtml(env.key) + '">';
37
+ const d = env.data;
38
+ switch (env.shape) {
39
+ case 'list': return head + listHtml(d.items) + '</section>';
40
+ case 'detail': return head + chefItemHtml(d.item) + '</section>';
41
+ case 'receipt': return head + '<div class="receipt">' + escapeHtml(String(d.message)) + '</div></section>';
42
+ case 'stat': {
43
+ const rows = Object.entries((d.metrics || {}));
44
+ return head + rows.map(([k, v]) => '<div class="stat"><b>' + v + '</b><span>' + escapeHtml(k) + '</span></div>').join('') + '</section>';
45
+ }
46
+ case 'analysis': return head + '<div class="analysis">' + escapeHtml(String(d.summary)).replace(/\n/g, '<br>') + '</div></section>';
47
+ case 'fallback': return head + '<div data-degraded="1">' + escapeHtml(String(d.reason)) + '</div></section>';
48
+ default: throw new ChefRenderError('CHEF_SHAPE_MISMATCH', '无法渲染形状:' + String(env.shape));
49
+ }
50
+ }
51
+ export function estimateBytes(html) { return Buffer.byteLength(html, 'utf8'); }
52
+ // 体积门:超限 throw(调用方走 fallback 形状显式降级,不静默截断)。
53
+ export function assertHtmlSize(html, max = CHEF_HTML_MAX_BYTES) {
54
+ if (estimateBytes(html) > max) {
55
+ throw new ChefRenderError('CHEF_HTML_TOO_LARGE', 'HTML 超体积:' + estimateBytes(html) + ' > ' + max);
56
+ }
57
+ }
58
+ // 共享标记填充(老家离线注入壳对应):CSS/HELPERS/CONTENT 三标记各恰出现 1 次,否则 throw。
59
+ export const SHARED_CSS_MARKER = '<!--SHARED-CSS-->';
60
+ export const SHARED_HELPERS_MARKER = '<!--SHARED-HELPERS-->';
61
+ export const CONTENT_MARKER = '<!--CONTENT-->';
62
+ export const SHARED_CSS = '.page{font-family:system-ui,sans-serif;max-width:720px;margin:0 auto;padding:12px}.item{border:1px solid #ddd;border-radius:8px;padding:8px;margin:8px 0}.item-head{display:flex;gap:8px;align-items:center}.badge{background:#eee;border-radius:4px;padding:0 6px}.amt{font-weight:700}.receipt{background:#f0fff0;border:1px solid #090;border-radius:8px;padding:12px}.stat{display:flex;gap:8px}.analysis{white-space:pre-wrap}.hm-empty{color:#888}';
63
+ export const SHARED_HELPERS = '<script>function copyItem(id){var e=document.getElementById(id);if(e&&navigator.clipboard){navigator.clipboard.writeText(e.innerText);}}</script>';
64
+ export function fillTemplate(template, contentHtml) {
65
+ for (const m of [SHARED_CSS_MARKER, SHARED_HELPERS_MARKER, CONTENT_MARKER]) {
66
+ if (template.split(m).length - 1 !== 1) {
67
+ throw new ChefRenderError('CHEF_MARKER_INVALID', '标记须恰出现 1 次:' + m);
68
+ }
69
+ }
70
+ return template
71
+ .split(SHARED_CSS_MARKER).join('<style>' + SHARED_CSS + '</style>')
72
+ .split(SHARED_HELPERS_MARKER).join(SHARED_HELPERS)
73
+ .split(CONTENT_MARKER).join(contentHtml);
74
+ }
75
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/render/html.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,GAAG,IAAI,CAAC;AAE9C,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC7H,CAAC;AAED,SAAS,OAAO,CAAC,CAA0B;IACzC,KAAK,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC;QAClE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACf,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC;YAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IACD,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;IAChB,OAAO,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACtD,CAAC;AAED,SAAS,MAAM,CAAC,CAA0B;IACxC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC;QACtG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACf,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,YAAY,CAAC,CAA0B;IAC9C,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,OAAO,+DAA+D,GAAG,KAAK,GAAG,4BAA4B,GAAG,IAAI,GAAG,qBAAqB,CAAC;AAC/I,CAAC;AAED,SAAS,QAAQ,CAAC,KAAgB;IAChC,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,kCAAkC,CAAC;IAC7D,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAA4B,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,kBAAkB,CAAC,GAAa;IAC9C,MAAM,IAAI,GAAG,yCAAyC,GAAG,GAAG,CAAC,KAAK,GAAG,cAAc,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IACjH,MAAM,CAAC,GAAG,GAAG,CAAC,IAA+B,CAAC;IAC9C,QAAQ,GAAG,CAAC,KAAK,EAAE,CAAC;QAClB,KAAK,MAAM,CAAC,CAAC,OAAO,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAkB,CAAC,GAAG,YAAY,CAAC;QACzE,KAAK,QAAQ,CAAC,CAAC,OAAO,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,IAA+B,CAAC,GAAG,YAAY,CAAC;QAC5F,KAAK,SAAS,CAAC,CAAC,OAAO,IAAI,GAAG,uBAAuB,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,kBAAkB,CAAC;QAC3G,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAA2B,CAAC,CAAC;YACzE,OAAO,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,uBAAuB,GAAG,CAAC,GAAG,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC;QAC3I,CAAC;QACD,KAAK,UAAU,CAAC,CAAC,OAAO,IAAI,GAAG,wBAAwB,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,kBAAkB,CAAC;QACpI,KAAK,UAAU,CAAC,CAAC,OAAO,IAAI,GAAG,yBAAyB,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,kBAAkB,CAAC;QAC7G,OAAO,CAAC,CAAC,MAAM,IAAI,eAAe,CAAC,qBAAqB,EAAE,SAAS,GAAG,MAAM,CAAE,GAA2B,CAAC,KAAK,CAAC,CAAC,CAAC;IACpH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY,IAAY,OAAO,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;AAE/F,4CAA4C;AAC5C,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,GAAG,GAAG,mBAAmB;IACpE,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;QAC9B,MAAM,IAAI,eAAe,CAAC,qBAAqB,EAAE,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,CAAC;IACpG,CAAC;AACH,CAAC;AAED,8DAA8D;AAC9D,MAAM,CAAC,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AACrD,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAuB,CAAC;AAC7D,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAE/C,MAAM,CAAC,MAAM,UAAU,GAAG,0bAA0b,CAAC;AACrd,MAAM,CAAC,MAAM,cAAc,GAAG,mJAAmJ,CAAC;AAElL,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,WAAmB;IAChE,KAAK,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,qBAAqB,EAAE,cAAc,CAAC,EAAE,CAAC;QAC3E,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,eAAe,CAAC,qBAAqB,EAAE,aAAa,GAAG,CAAC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IACD,OAAO,QAAQ;SACZ,KAAK,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;SAClE,KAAK,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC;SACjD,KAAK,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { ChefRenderError } from './errors.js';
2
+ export { CHEF_KEY_SHAPES, chefShapeFor, buildChefEnvelope, parseChefEnvelope } from './envelope.js';
3
+ export { CHEF_HTML_MAX_BYTES, escapeHtml, renderEnvelopeHtml, estimateBytes, assertHtmlSize, SHARED_CSS_MARKER, SHARED_HELPERS_MARKER, CONTENT_MARKER, SHARED_CSS, SHARED_HELPERS, fillTemplate } from './html.js';
4
+ export { CHEF_TEMPLATES, templateFor, loadTemplate } from './templates.js';
5
+ export type { ChefTemplate } from './templates.js';
6
+ export { toRecipeItem, recipeDetail, buildRecipeSearch, buildRecipeReceipt, buildCookingRun, buildShopping, buildHistoryRecord, buildHistoryQuery, toHistoryItem, buildHelpItems } from './views.js';
7
+ export type { RecipeItem, RecipeDetail, RecipeHistoryStats, CookingStep, HelpItem } from './views.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACpG,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,cAAc,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACnN,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC3E,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,aAAa,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACrM,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,kBAAkB,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { ChefRenderError } from './errors.js';
2
+ export { CHEF_KEY_SHAPES, chefShapeFor, buildChefEnvelope, parseChefEnvelope } from './envelope.js';
3
+ export { CHEF_HTML_MAX_BYTES, escapeHtml, renderEnvelopeHtml, estimateBytes, assertHtmlSize, SHARED_CSS_MARKER, SHARED_HELPERS_MARKER, CONTENT_MARKER, SHARED_CSS, SHARED_HELPERS, fillTemplate } from './html.js';
4
+ export { CHEF_TEMPLATES, templateFor, loadTemplate } from './templates.js';
5
+ export { toRecipeItem, recipeDetail, buildRecipeSearch, buildRecipeReceipt, buildCookingRun, buildShopping, buildHistoryRecord, buildHistoryQuery, toHistoryItem, buildHelpItems } from './views.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/render/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACpG,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,cAAc,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACnN,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,aAAa,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}