@stylewx/service 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 (49) hide show
  1. package/dist/analyze.d.ts +18 -0
  2. package/dist/analyze.d.ts.map +1 -0
  3. package/dist/analyze.js +109 -0
  4. package/dist/analyze.js.map +1 -0
  5. package/dist/errors.d.ts +18 -0
  6. package/dist/errors.d.ts.map +1 -0
  7. package/dist/errors.js +53 -0
  8. package/dist/errors.js.map +1 -0
  9. package/dist/generate.d.ts +32 -0
  10. package/dist/generate.d.ts.map +1 -0
  11. package/dist/generate.js +229 -0
  12. package/dist/generate.js.map +1 -0
  13. package/dist/index.d.ts +10 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +10 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/llm.d.ts +54 -0
  18. package/dist/llm.d.ts.map +1 -0
  19. package/dist/llm.js +190 -0
  20. package/dist/llm.js.map +1 -0
  21. package/dist/optimize.d.ts +12 -0
  22. package/dist/optimize.d.ts.map +1 -0
  23. package/dist/optimize.js +21 -0
  24. package/dist/optimize.js.map +1 -0
  25. package/dist/publish.d.ts +13 -0
  26. package/dist/publish.d.ts.map +1 -0
  27. package/dist/publish.js +19 -0
  28. package/dist/publish.js.map +1 -0
  29. package/dist/render.d.ts +17 -0
  30. package/dist/render.d.ts.map +1 -0
  31. package/dist/render.js +35 -0
  32. package/dist/render.js.map +1 -0
  33. package/dist/service.test.d.ts +2 -0
  34. package/dist/service.test.d.ts.map +1 -0
  35. package/dist/service.test.js +163 -0
  36. package/dist/service.test.js.map +1 -0
  37. package/dist/theme-store.d.ts +28 -0
  38. package/dist/theme-store.d.ts.map +1 -0
  39. package/dist/theme-store.js +93 -0
  40. package/dist/theme-store.js.map +1 -0
  41. package/dist/theme-store.test.d.ts +2 -0
  42. package/dist/theme-store.test.d.ts.map +1 -0
  43. package/dist/theme-store.test.js +70 -0
  44. package/dist/theme-store.test.js.map +1 -0
  45. package/dist/themes.d.ts +15 -0
  46. package/dist/themes.d.ts.map +1 -0
  47. package/dist/themes.js +38 -0
  48. package/dist/themes.js.map +1 -0
  49. package/package.json +36 -0
@@ -0,0 +1,18 @@
1
+ export type ContentType = 'tech' | 'business' | 'literary' | 'government' | 'academic' | 'general';
2
+ export interface AnalyzeResult {
3
+ content: {
4
+ type: ContentType;
5
+ typeLabel: string;
6
+ tone: string;
7
+ readingMinutes: number;
8
+ wordCount: number;
9
+ };
10
+ suggestedTheme: {
11
+ name: string;
12
+ description: string;
13
+ reason: string;
14
+ };
15
+ analysisText: string;
16
+ }
17
+ export declare function analyzeArticle(markdown: string): AnalyzeResult;
18
+ //# sourceMappingURL=analyze.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,UAAU,GACV,UAAU,GACV,YAAY,GACZ,UAAU,GACV,SAAS,CAAA;AAyDb,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE;QACP,IAAI,EAAE,WAAW,CAAA;QACjB,SAAS,EAAE,MAAM,CAAA;QACjB,IAAI,EAAE,MAAM,CAAA;QACZ,cAAc,EAAE,MAAM,CAAA;QACtB,SAAS,EAAE,MAAM,CAAA;KAClB,CAAA;IACD,cAAc,EAAE;QACd,IAAI,EAAE,MAAM,CAAA;QACZ,WAAW,EAAE,MAAM,CAAA;QACnB,MAAM,EAAE,MAAM,CAAA;KACf,CAAA;IACD,YAAY,EAAE,MAAM,CAAA;CACrB;AAaD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAuC9D"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * 文章内容分析(确定性启发式,无需 LLM):
3
+ * 推断内容类型、情绪基调、建议主题方向、估算阅读时长。
4
+ * 该结论同时作为 generate_theme 的输入。
5
+ */
6
+ import { countWords, estimateReadingMinutes } from '@stylewx/core';
7
+ const TYPE_DEFS = {
8
+ tech: {
9
+ label: '技术 / 工程',
10
+ keywords: [
11
+ '代码', '函数', '开发', '接口', '部署', '前端', '后端', '数据库', '算法',
12
+ '编程', 'docker', 'api', 'git', 'rendered', 'variable', 'function', 'framework',
13
+ '性能', '优化', '架构', '微服务', 'serverless',
14
+ ],
15
+ themeName: 'tech-minimal',
16
+ themeReason: '技术内容适合冷峻清晰、留白充分的科技极简风。',
17
+ },
18
+ business: {
19
+ label: '商业 / 财经',
20
+ keywords: [
21
+ '营收', '增长', '市场', '投资', '估值', '融资', '财报', '战略', '品牌',
22
+ '客户', '商业', '行业', '公司', '产品', '利润', '收入', '营销', '招聘',
23
+ ],
24
+ themeName: 'business',
25
+ themeReason: '商业财经适合稳重藏蓝 + 金色点缀的商务风。',
26
+ },
27
+ literary: {
28
+ label: '文艺 / 生活',
29
+ keywords: [
30
+ '散文', '生活', '感悟', '旅行', '回忆', '情绪', '诗歌', '阅读', '人生',
31
+ '月光', '黄昏', '孤独', '温暖', '岁', '字', '篇',
32
+ ],
33
+ themeName: 'magazine',
34
+ themeReason: '文艺人文适合暖棕衬线、宽松行距的杂志风。',
35
+ },
36
+ government: {
37
+ label: '政务 / 官方',
38
+ keywords: [
39
+ '政务', '党建', '党员', '党委', '通知', '政策', '文件', '会议', '落实',
40
+ '精神', '人民', '服务', '基层', '政府', '法规', '条例', '号召',
41
+ ],
42
+ themeName: 'gov-red',
43
+ themeReason: '政务发布适合庄重中国红的官方风。',
44
+ },
45
+ academic: {
46
+ label: '学术 / 研究',
47
+ keywords: [
48
+ '研究', '论文', '文献', '实验', '数据', '假设', '结论', '综述', '方法论',
49
+ '学位', '期刊', '引用', '样本', '分析', '验证',
50
+ ],
51
+ themeName: 'academic',
52
+ themeReason: '学术内容适合中性克制、高可读的学术风。',
53
+ },
54
+ general: {
55
+ label: '通用',
56
+ keywords: [],
57
+ themeName: 'tech-minimal',
58
+ themeReason: '未能明确归类,推荐通用的科技极简风。',
59
+ },
60
+ };
61
+ function detectTone(markdown) {
62
+ const text = markdown.toLowerCase();
63
+ const positive = ['恭喜', '突破', '成功', '增长', '创新', '利好', '高效', '欣喜', '感动', '温暖'];
64
+ const negative = ['风险', '失败', '危机', '下跌', '亏损', '担忧', '警示', '压力', '争议'];
65
+ const po = positive.filter((k) => text.includes(k)).length;
66
+ const ne = negative.filter((k) => text.includes(k)).length;
67
+ if (po > ne)
68
+ return '积极 / 建设性';
69
+ if (ne > po)
70
+ return '严肃 / 审慎';
71
+ return '中性 / 客观';
72
+ }
73
+ export function analyzeArticle(markdown) {
74
+ const text = markdown.toLowerCase();
75
+ const wordCount = countWords(markdown);
76
+ const readingMinutes = estimateReadingMinutes(markdown);
77
+ let best = 'general';
78
+ let bestScore = 0;
79
+ for (const [type, def] of Object.entries(TYPE_DEFS)) {
80
+ if (type === 'general')
81
+ continue;
82
+ const score = def.keywords.filter((k) => text.includes(k)).length;
83
+ if (score > bestScore) {
84
+ bestScore = score;
85
+ best = type;
86
+ }
87
+ }
88
+ const def = TYPE_DEFS[best];
89
+ const tone = detectTone(markdown);
90
+ const analysisText = `内容类型:${def.label};情绪基调:${tone};` +
91
+ `字数约 ${wordCount},预计阅读 ${readingMinutes} 分钟;` +
92
+ `建议主题:${def.themeName}(${def.themeReason})`;
93
+ return {
94
+ content: {
95
+ type: best,
96
+ typeLabel: def.label,
97
+ tone,
98
+ readingMinutes,
99
+ wordCount,
100
+ },
101
+ suggestedTheme: {
102
+ name: def.themeName,
103
+ description: def.themeReason,
104
+ reason: def.themeReason,
105
+ },
106
+ analysisText,
107
+ };
108
+ }
109
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * 统一的服务/工具错误格式:{ error: { code, message, hint } }。
3
+ * hint 面向 Agent,明确告诉下一步该怎么办。
4
+ */
5
+ import { z } from 'zod';
6
+ export declare const serviceErrorSchema: z.ZodObject<{
7
+ error: z.ZodObject<{
8
+ code: z.ZodString;
9
+ message: z.ZodString;
10
+ hint: z.ZodString;
11
+ }, z.core.$strip>;
12
+ }, z.core.$strip>;
13
+ export type ServiceError = z.infer<typeof serviceErrorSchema>;
14
+ /** 构建一个符合统一格式的错误对象。 */
15
+ export declare function serviceError(code: string, message: string, hint: string): ServiceError;
16
+ /** 把任意异常包装成统一错误(尽量保留中文可读提示)。 */
17
+ export declare function asServiceError(error: unknown, fallbackCode?: string): ServiceError;
18
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,kBAAkB;;;;;;iBAM7B,CAAA;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAE7D,uBAAuB;AACvB,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CAQtF;AAED,gCAAgC;AAChC,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,SAAmB,GAAG,YAAY,CAkB5F"}
package/dist/errors.js ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 统一的服务/工具错误格式:{ error: { code, message, hint } }。
3
+ * hint 面向 Agent,明确告诉下一步该怎么办。
4
+ */
5
+ import { z } from 'zod';
6
+ export const serviceErrorSchema = z.object({
7
+ error: z.object({
8
+ code: z.string(),
9
+ message: z.string(),
10
+ hint: z.string(),
11
+ }),
12
+ });
13
+ /** 构建一个符合统一格式的错误对象。 */
14
+ export function serviceError(code, message, hint) {
15
+ return {
16
+ error: {
17
+ code,
18
+ message,
19
+ hint,
20
+ },
21
+ };
22
+ }
23
+ /** 把任意异常包装成统一错误(尽量保留中文可读提示)。 */
24
+ export function asServiceError(error, fallbackCode = 'internal_error') {
25
+ // 若是已符合统一格式的错误对象({ error: { code, message, hint } }),原样保留,避免被 String 成 [object Object]
26
+ if (error && typeof error === 'object' && 'error' in error) {
27
+ const e = error.error;
28
+ if (e && typeof e.code === 'string')
29
+ return { error: e };
30
+ }
31
+ if (error instanceof Error) {
32
+ return serviceError(codeFromMessage(error.message) ?? fallbackCode, error.message, '请根据错误信息修正输入后重试;如无法解决,请检查配置或联系管理员。');
33
+ }
34
+ return serviceError(fallbackCode, typeof error === 'string' ? error : String(error), '未预期的错误,请重试或检查输入。');
35
+ }
36
+ const CODE_PATTERNS = [
37
+ [/缺少|missing/i, 'missing_config'],
38
+ [/主题.*不合法|无法渲染|白名单/i, 'invalid_theme'],
39
+ [/标题|title/i, 'missing_title'],
40
+ [/封面|coverimage/i, 'missing_cover'],
41
+ [/凭证|凭据|app_id|app_secret/i, 'missing_wechat_credential'],
42
+ [/errcode/i, 'wechat_api_error'],
43
+ [/external|外链/i, 'external_image'],
44
+ [/playwright install|chromium/i, 'browser_not_installed'],
45
+ ];
46
+ function codeFromMessage(message) {
47
+ for (const [regex, code] of CODE_PATTERNS) {
48
+ if (regex.test(message))
49
+ return code;
50
+ }
51
+ return undefined;
52
+ }
53
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;QACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;QACnB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;KACjB,CAAC;CACH,CAAC,CAAA;AAIF,uBAAuB;AACvB,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,OAAe,EAAE,IAAY;IACtE,OAAO;QACL,KAAK,EAAE;YACL,IAAI;YACJ,OAAO;YACP,IAAI;SACL;KACF,CAAA;AACH,CAAC;AAED,gCAAgC;AAChC,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,YAAY,GAAG,gBAAgB;IAC5E,uFAAuF;IACvF,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,IAAI,KAAK,EAAE,CAAC;QAC3D,MAAM,CAAC,GAAI,KAAsB,CAAC,KAAK,CAAA;QACvC,IAAI,CAAC,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,CAAA;IAC1D,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,YAAY,CACjB,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,YAAY,EAC9C,KAAK,CAAC,OAAO,EACb,mCAAmC,CACpC,CAAA;IACH,CAAC;IACD,OAAO,YAAY,CACjB,YAAY,EACZ,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACjD,kBAAkB,CACnB,CAAA;AACH,CAAC;AAED,MAAM,aAAa,GAA4B;IAC7C,CAAC,aAAa,EAAE,gBAAgB,CAAC;IACjC,CAAC,mBAAmB,EAAE,eAAe,CAAC;IACtC,CAAC,WAAW,EAAE,eAAe,CAAC;IAC9B,CAAC,gBAAgB,EAAE,eAAe,CAAC;IACnC,CAAC,0BAA0B,EAAE,2BAA2B,CAAC;IACzD,CAAC,UAAU,EAAE,kBAAkB,CAAC;IAChC,CAAC,cAAc,EAAE,gBAAgB,CAAC;IAClC,CAAC,8BAA8B,EAAE,uBAAuB,CAAC;CAC1D,CAAA;AAED,SAAS,eAAe,CAAC,OAAe;IACtC,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,aAAa,EAAE,CAAC;QAC1C,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAA;IACtC,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { Theme } from '@stylewx/theme';
2
+ import type { LlmClient } from './llm.js';
3
+ export interface GenerateThemeParams {
4
+ /** 文章全文(可选)。提供时会先让模型做一次内容分析。 */
5
+ article?: string;
6
+ /** 风格描述 / 设计需求(可选)。 */
7
+ prompt?: string;
8
+ /** 基础主题名(在预置主题基础上微调)。 */
9
+ baseTheme?: string;
10
+ }
11
+ export interface GenerateThemeResult {
12
+ theme: Theme;
13
+ /** 是否因多次修复失败而降级到预置主题。 */
14
+ fallback: boolean;
15
+ /** 尝试次数(含最终成功的那次)。 */
16
+ repairAttempts: number;
17
+ /** 生成失败时的修复建议(fallback=true 时有用)。 */
18
+ errorDetail?: string;
19
+ /** 内容分析结论(提供 article 时返回)。 */
20
+ analysis?: {
21
+ contentType: string;
22
+ tone: string;
23
+ industry: string;
24
+ designDirection: string;
25
+ text: string;
26
+ };
27
+ /** 用该主题渲染内置示例文章的截图(若已安装 Chromium)。 */
28
+ previewPng?: Buffer;
29
+ }
30
+ export declare const SAMPLE_ARTICLE = "# \u793A\u4F8B\u6587\u7AE0\n\n## \u4E00\u3001\u80CC\u666F\n\n\u5728\u5185\u5BB9\u521B\u4F5C\u4E2D\uFF0C\u6392\u7248\u5F80\u5F80\u88AB\u4F4E\u4F30\u3002\u597D\u7684\u6392\u7248\u80FD**\u663E\u8457\u63D0\u5347**\u9605\u8BFB\u4F53\u9A8C\uFF0C\u4E5F\u80FD\u8BA9\u54C1\u724C\u66F4\u6709\u8FA8\u8BC6\u5EA6\u3002\n\n> \u8FD9\u53EA\u662F\u4E00\u4E2A\u7528\u4E8E\u9A8C\u8BC1\u4E3B\u9898\u6548\u679C\u7684\u793A\u4F8B\u6BB5\u843D\u3002\n\n## \u4E8C\u3001\u6838\u5FC3\u8981\u70B9\n\n- \u4E00\u81F4\u6027\uFF1A\u5168\u5C40\u7EDF\u4E00\u5B57\u53F7\u3001\u884C\u8DDD\u4E0E\u914D\u8272\n- \u514B\u5236\uFF1A\u88C5\u9970\u5143\u7D20\u5B81\u7F3A\u6BCB\u6EE5\n- \u53EF\u8BFB\uFF1A\u5728\u624B\u673A\u4E0A\uFF0C\u7EBF\u6761\u4E0E\u7559\u767D\u80DC\u8FC7\u70AB\u6280\n\n## \u4E09\u3001\u4EE3\u7801\u793A\u4F8B\n\n```ts\nexport function hello(name: string): string {\n return `Hello, ${name}`\n}\n```\n\n![\u793A\u4F8B\u914D\u56FE](https://mmbiz.qpic.cn/sz_mmbiz_png/placeholder/0?wx_fmt=png)\n\n---\n\n\u7ED3\u5C3E\u793A\u4F8B\u6587\u5B57\u3002\n";
31
+ export declare function generateTheme(params: GenerateThemeParams, llm: LlmClient): Promise<GenerateThemeResult>;
32
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAG3C,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,UAAU,CAAA;AAErD,MAAM,WAAW,mBAAmB;IAClC,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uBAAuB;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,KAAK,CAAA;IACZ,yBAAyB;IACzB,QAAQ,EAAE,OAAO,CAAA;IACjB,sBAAsB;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE;QACT,WAAW,EAAE,MAAM,CAAA;QACnB,IAAI,EAAE,MAAM,CAAA;QACZ,QAAQ,EAAE,MAAM,CAAA;QAChB,eAAe,EAAE,MAAM,CAAA;QACvB,IAAI,EAAE,MAAM,CAAA;KACb,CAAA;IACD,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAID,eAAO,MAAM,cAAc,wgCA2B1B,CAAA;AAmED,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,EAC3B,GAAG,EAAE,SAAS,GACb,OAAO,CAAC,mBAAmB,CAAC,CA+H9B"}
@@ -0,0 +1,229 @@
1
+ /**
2
+ * AI 主题生成:LLM 结构化输出主题 JSON → zod + 白名单校验 → 失败自动修复(最多 2 次重试)
3
+ * → 仍失败则降级返回最接近的预置主题并标记 fallback: true。
4
+ * 生成成功后自动渲染一篇内置示例文章并截图。
5
+ */
6
+ import { z } from 'zod';
7
+ import { themeToJsonSchema, validateTheme, getPresetTheme, PRESET_THEMES, } from '@stylewx/theme';
8
+ import { analyzeArticle } from './analyze.js';
9
+ import { renderPreview } from './render.js';
10
+ const MAX_REPAIRS = 2;
11
+ export const SAMPLE_ARTICLE = `# 示例文章
12
+
13
+ ## 一、背景
14
+
15
+ 在内容创作中,排版往往被低估。好的排版能**显著提升**阅读体验,也能让品牌更有辨识度。
16
+
17
+ > 这只是一个用于验证主题效果的示例段落。
18
+
19
+ ## 二、核心要点
20
+
21
+ - 一致性:全局统一字号、行距与配色
22
+ - 克制:装饰元素宁缺毋滥
23
+ - 可读:在手机上,线条与留白胜过炫技
24
+
25
+ ## 三、代码示例
26
+
27
+ \`\`\`ts
28
+ export function hello(name: string): string {
29
+ return \`Hello, \${name}\`
30
+ }
31
+ \`\`\`
32
+
33
+ ![示例配图](https://mmbiz.qpic.cn/sz_mmbiz_png/placeholder/0?wx_fmt=png)
34
+
35
+ ---
36
+
37
+ 结尾示例文字。
38
+ `;
39
+ const contentAnalysisSchema = z.object({
40
+ contentType: z.enum(['tech', 'business', 'literary', 'government', 'academic', 'general']),
41
+ tone: z.string(),
42
+ industry: z.string(),
43
+ designDirection: z.string(),
44
+ });
45
+ function hexToRgb(hex) {
46
+ const m = /^#([0-9a-f]{6})$/i.exec(hex);
47
+ if (!m?.[1])
48
+ return null;
49
+ const v = m[1];
50
+ return [
51
+ parseInt(v.slice(0, 2), 16),
52
+ parseInt(v.slice(2, 4), 16),
53
+ parseInt(v.slice(4, 6), 16),
54
+ ];
55
+ }
56
+ function colorDistance(a, b) {
57
+ const ra = hexToRgb(a);
58
+ const rb = hexToRgb(b);
59
+ if (!ra || !rb)
60
+ return Number.MAX_SAFE_INTEGER;
61
+ return Math.sqrt((ra[0] - rb[0]) ** 2 + (ra[1] - rb[1]) ** 2 + (ra[2] - rb[2]) ** 2);
62
+ }
63
+ /** 选择与「目标主色」最接近的预置主题;无法比较时回退到首选。 */
64
+ function closestPresetTheme(targetColor) {
65
+ if (targetColor) {
66
+ let best = PRESET_THEMES[0];
67
+ let bestDist = Number.MAX_SAFE_INTEGER;
68
+ for (const theme of PRESET_THEMES) {
69
+ const dist = colorDistance(theme.tokens.primaryColor, targetColor);
70
+ if (dist < bestDist) {
71
+ bestDist = dist;
72
+ best = theme;
73
+ }
74
+ }
75
+ return best;
76
+ }
77
+ return PRESET_THEMES[0];
78
+ }
79
+ function buildThemeSystemPrompt(baseTheme) {
80
+ const baseNotes = baseTheme
81
+ ? `请以预置主题「${baseTheme}」为基础进行微调,保持其整体气质,但可调整配色与细节。\n`
82
+ : '';
83
+ return `你是资深的公众号排版设计专家。请根据用户需求,输出一个「主题 JSON」。
84
+ 主题 JSON 必须严格符合给定的 JSON Schema。
85
+ 硬性约束:
86
+ 1. tokens 必须提供 primaryColor(十六进制)、textColor、fontSize(如 15px)、lineHeight(数字 0.8~4)、fontFamily、spacing.block。
87
+ 2. blocks 包含 h1,h2,h3,p,blockquote,ul,ol,li,code,pre,img,figcaption,hr,a,strong 十六个对象;每个对象的键是 CSS 属性。
88
+ 3. 所有 CSS 属性必须在微信白名单内(禁止 position/transform/animation/transition/float/box-shadow/伪类/外部字体等,这些微信会过滤)。
89
+ 4. 值可以用 {{primaryColor}}、{{textColor}}、{{fontSize}}、{{spacing.block}}、{{lineHeight}} 等 token 引用。
90
+ 5. description 请用中文写清楚风格与适用场景(供 Agent 选择时阅读),不要太短。
91
+ 6. 只输出 JSON 本身,不要输出任何多余文字或代码块标记。
92
+ ${baseNotes}`;
93
+ }
94
+ function themePromptForAttempt(instructions, fixNotes) {
95
+ const user = fixNotes
96
+ ? `${instructions}\n\n上一版主题未通过校验,请修复以下问题后再输出完整主题 JSON(只输出 JSON,不要解释):\n${fixNotes}`
97
+ : instructions;
98
+ return user;
99
+ }
100
+ export async function generateTheme(params, llm) {
101
+ const instructions = params.prompt?.trim() || params.article?.trim() || '生成一款简洁大方、适合公众号的文章主题。';
102
+ // 若提供全文,先让模型做内容分析(结论随响应返回)
103
+ let analysis;
104
+ let analysisText = '';
105
+ if (params.article && params.article.trim()) {
106
+ const heuristic = analyzeArticle(params.article);
107
+ analysisText = heuristic.analysisText;
108
+ try {
109
+ const raw = await llm.completeJson([
110
+ { role: 'system', content: '你是内容分析助手。请分析下面文章的类型、情绪基调、所属行业,并给出排版设计方向建议(用中文,简洁)。' },
111
+ { role: 'user', content: params.article.slice(0, 6000) },
112
+ ], {
113
+ schema: z.toJSONSchema(contentAnalysisSchema),
114
+ name: 'content_analysis',
115
+ temperature: 0.3,
116
+ maxTokens: 500,
117
+ });
118
+ const parsed = contentAnalysisSchema.parse(raw);
119
+ analysis = {
120
+ contentType: parsed.contentType,
121
+ tone: parsed.tone,
122
+ industry: parsed.industry,
123
+ designDirection: parsed.designDirection,
124
+ text: `内容类型:${parsed.contentType};基调:${parsed.tone};行业:${parsed.industry};` +
125
+ `排版方向:${parsed.designDirection}\n` +
126
+ `(启发式分析:${heuristic.analysisText})`,
127
+ };
128
+ }
129
+ catch {
130
+ // LLM 分析失败则回退到启发式结论
131
+ analysis = {
132
+ contentType: heuristic.content.type,
133
+ tone: heuristic.content.tone,
134
+ industry: '',
135
+ designDirection: heuristic.suggestedTheme.reason,
136
+ text: heuristic.analysisText,
137
+ };
138
+ }
139
+ }
140
+ const scheme = themeToJsonSchema();
141
+ // 预置主题作为参考,给模型更稳定的起点
142
+ const base = params.baseTheme ? getPresetTheme(params.baseTheme) : undefined;
143
+ const genInstructions = `${instructions}\n` +
144
+ (params.article && params.article.trim() ? `\n文章全文(节选):\n${params.article.slice(0, 6000)}\n` : '') +
145
+ (analysis ? `\n内容分析结论:\n${analysis.text}\n` : '') +
146
+ (base ? `\n基础主题 tokens 参考:${JSON.stringify(base.tokens)}\n` : '');
147
+ const messages = [
148
+ { role: 'system', content: buildThemeSystemPrompt(params.baseTheme) },
149
+ { role: 'user', content: themePromptForAttempt(genInstructions) },
150
+ ];
151
+ let theme = null;
152
+ let errorDetail = '';
153
+ let repairAttempts = 0;
154
+ let lastTargetColor;
155
+ for (let attempt = 0; attempt <= MAX_REPAIRS; attempt += 1) {
156
+ try {
157
+ const raw = await llm.completeJson(messages, {
158
+ schema: scheme,
159
+ name: 'mp_style_theme',
160
+ temperature: 0.5,
161
+ maxTokens: 3200,
162
+ });
163
+ const candidate = assignThemeDefaults(raw);
164
+ lastTargetColor = candidate.tokens?.primaryColor;
165
+ const validation = validateTheme(candidate);
166
+ if (validation.ok && validation.theme) {
167
+ theme = validation.theme;
168
+ repairAttempts = attempt + 1;
169
+ break;
170
+ }
171
+ // 失败 → 收集修复建议,回传给模型再试
172
+ const issues = validation.issues
173
+ .map((i) => `- ${i.path}: ${i.message}`)
174
+ .join('\n');
175
+ errorDetail = issues;
176
+ messages.push({
177
+ role: 'assistant',
178
+ content: '(上一版本无效主题)',
179
+ });
180
+ messages.push({
181
+ role: 'user',
182
+ content: themePromptForAttempt(genInstructions, `请修复:\n${issues}`),
183
+ });
184
+ }
185
+ catch (error) {
186
+ errorDetail = error instanceof Error ? error.message : String(error);
187
+ messages.push({
188
+ role: 'user',
189
+ content: themePromptForAttempt(genInstructions, `请重新输出一个完整、合法且完全符合 Schema 的主题 JSON。`),
190
+ });
191
+ }
192
+ }
193
+ let fallback = false;
194
+ if (!theme) {
195
+ fallback = true;
196
+ repairAttempts = MAX_REPAIRS + 1;
197
+ theme = closestPresetTheme(lastTargetColor);
198
+ }
199
+ // 自动渲染内置示例文章并截图(截图失败不致命)
200
+ let previewPng;
201
+ try {
202
+ const preview = await renderPreview(SAMPLE_ARTICLE, theme, { includeScreenshot: true });
203
+ previewPng = preview.screenshotPng;
204
+ }
205
+ catch {
206
+ // ignore
207
+ }
208
+ return {
209
+ theme,
210
+ fallback,
211
+ repairAttempts,
212
+ errorDetail: fallback ? errorDetail || '主题生成多次失败,已降级为预置主题。' : undefined,
213
+ analysis,
214
+ previewPng,
215
+ };
216
+ }
217
+ /** 为 LLM 可能缺省的字段补上合理默认值,提高首次通过率。 */
218
+ function assignThemeDefaults(raw) {
219
+ const fallbackTheme = PRESET_THEMES[0];
220
+ const tokens = raw.tokens ?? fallbackTheme.tokens;
221
+ const blocks = raw.blocks ?? fallbackTheme.blocks;
222
+ return {
223
+ name: raw.name ?? '自定义主题',
224
+ description: raw.description ?? 'AI 生成的主题。',
225
+ tokens,
226
+ blocks,
227
+ };
228
+ }
229
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export * from './errors.js';
2
+ export * from './themes.js';
3
+ export * from './theme-store.js';
4
+ export * from './analyze.js';
5
+ export * from './render.js';
6
+ export * from './publish.js';
7
+ export * from './llm.js';
8
+ export * from './generate.js';
9
+ export * from './optimize.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export * from './errors.js';
2
+ export * from './themes.js';
3
+ export * from './theme-store.js';
4
+ export * from './analyze.js';
5
+ export * from './render.js';
6
+ export * from './publish.js';
7
+ export * from './llm.js';
8
+ export * from './generate.js';
9
+ export * from './optimize.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,eAAe,CAAA;AAC7B,cAAc,eAAe,CAAA"}
package/dist/llm.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ /**
2
+ * OpenAI 兼容的 LLM 客户端(结构化输出)。
3
+ * 通过 JSON Schema 约束模型输出,供 generate_theme 使用。
4
+ *
5
+ * 环境变量:
6
+ * - LLM_BASE_URL OpenAI 兼容 API 基地址(如 https://api.openai.com/v1,或 opencode go 的 https://opencode.ai/zen/go/v1)
7
+ * - LLM_API_KEY API 密钥
8
+ * - LLM_MODEL 模型名
9
+ * - LLM_API_STYLE 端点风格:'chat'(/chat/completions,默认)或 'responses'(/responses,如 opencode go)
10
+ * 任何凭据都只从环境读取,绝不硬编码。
11
+ */
12
+ export type ApiStyle = 'chat' | 'responses';
13
+ export interface LlmConfig {
14
+ baseUrl: string;
15
+ apiKey: string;
16
+ model: string;
17
+ apiStyle?: ApiStyle;
18
+ fetchImpl?: typeof fetch;
19
+ }
20
+ export interface LlmMessage {
21
+ role: 'system' | 'user' | 'assistant';
22
+ content: string;
23
+ }
24
+ export interface LlmJsonOptions {
25
+ /** 用于约束输出的 JSON Schema。 */
26
+ schema: Record<string, unknown>;
27
+ /** 响应格式名称(用于 json_schema)。 */
28
+ name: string;
29
+ temperature?: number;
30
+ maxTokens?: number;
31
+ }
32
+ /** 从环境变量加载 LLM 配置;缺少关键项或 style 不合法时给出明确错误。 */
33
+ export declare function loadLlmConfigFromEnv(env?: NodeJS.ProcessEnv): LlmConfig;
34
+ export declare class LlmClient {
35
+ private readonly config;
36
+ private readonly fetchImpl;
37
+ constructor(config: LlmConfig);
38
+ private get apiStyle();
39
+ /** 组装 JSON 结构输出请求体(按端点风格差异)。 */
40
+ private buildJsonPayload;
41
+ /**
42
+ * 请求结构化 JSON 输出。
43
+ * 优先用 json_schema 约束模型;若端点报错,则退化到普通文本输出并从文本中提取 JSON。
44
+ */
45
+ completeJson(messages: LlmMessage[], options: LlmJsonOptions): Promise<unknown>;
46
+ /** 请求纯文本输出。 */
47
+ completeText(messages: LlmMessage[], options?: {
48
+ temperature?: number;
49
+ maxTokens?: number;
50
+ }): Promise<string>;
51
+ private endpoint;
52
+ private request;
53
+ }
54
+ //# sourceMappingURL=llm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAA;AAE3C,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAA;IACrC,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,8CAA8C;AAC9C,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,SAAS,CAgBpF;AAWD,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;gBAE5B,MAAM,EAAE,SAAS;IAK7B,OAAO,KAAK,QAAQ,GAEnB;IAED,gCAAgC;IAChC,OAAO,CAAC,gBAAgB;IA4CxB;;;OAGG;IACG,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAWrF,eAAe;IACT,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,OAAO,GAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAsBvH,OAAO,CAAC,QAAQ;YAKF,OAAO;CAuBtB"}