@gangtiser/md2wechat 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.
- package/LICENSE +5 -0
- package/README.md +105 -0
- package/dist/src/cli/index.d.ts +2 -0
- package/dist/src/cli/index.js +251 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/config/config.d.ts +9 -0
- package/dist/src/config/config.js +11 -0
- package/dist/src/config/config.js.map +1 -0
- package/dist/src/core/article.d.ts +20 -0
- package/dist/src/core/article.js +81 -0
- package/dist/src/core/article.js.map +1 -0
- package/dist/src/core/envelope.d.ts +14 -0
- package/dist/src/core/envelope.js +34 -0
- package/dist/src/core/envelope.js.map +1 -0
- package/dist/src/core/errors.d.ts +7 -0
- package/dist/src/core/errors.js +22 -0
- package/dist/src/core/errors.js.map +1 -0
- package/dist/src/core/html.d.ts +2 -0
- package/dist/src/core/html.js +18 -0
- package/dist/src/core/html.js.map +1 -0
- package/dist/src/core/io.d.ts +11 -0
- package/dist/src/core/io.js +28 -0
- package/dist/src/core/io.js.map +1 -0
- package/dist/src/images/generate.d.ts +17 -0
- package/dist/src/images/generate.js +28 -0
- package/dist/src/images/generate.js.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/inspect/inspect.d.ts +18 -0
- package/dist/src/inspect/inspect.js +31 -0
- package/dist/src/inspect/inspect.js.map +1 -0
- package/dist/src/mcp/server.d.ts +1 -0
- package/dist/src/mcp/server.js +25 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/mcp/tools.d.ts +19 -0
- package/dist/src/mcp/tools.js +190 -0
- package/dist/src/mcp/tools.js.map +1 -0
- package/dist/src/openai/client.d.ts +34 -0
- package/dist/src/openai/client.js +101 -0
- package/dist/src/openai/client.js.map +1 -0
- package/dist/src/preview/preview.d.ts +2 -0
- package/dist/src/preview/preview.js +21 -0
- package/dist/src/preview/preview.js.map +1 -0
- package/dist/src/renderer/render.d.ts +19 -0
- package/dist/src/renderer/render.js +36 -0
- package/dist/src/renderer/render.js.map +1 -0
- package/dist/src/themes/builtin.d.ts +2 -0
- package/dist/src/themes/builtin.js +21 -0
- package/dist/src/themes/builtin.js.map +1 -0
- package/dist/src/themes/registry.d.ts +18 -0
- package/dist/src/themes/registry.js +130 -0
- package/dist/src/themes/registry.js.map +1 -0
- package/package.json +55 -0
- package/skills/md2wechat/SKILL.md +52 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const builtinThemes = [
|
|
2
|
+
{
|
|
3
|
+
id: "default",
|
|
4
|
+
name: "Default",
|
|
5
|
+
description: "Clean WeChat article typography with balanced spacing.",
|
|
6
|
+
prompt: "Use clean, readable WeChat Official Account typography with inline styles, moderate spacing, and restrained accent color."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
id: "editorial",
|
|
10
|
+
name: "Editorial",
|
|
11
|
+
description: "Magazine-like headings and strong pull quotes.",
|
|
12
|
+
prompt: "Use editorial magazine pacing, strong headings, pull quotes, and inline styles compatible with WeChat."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: "minimal",
|
|
16
|
+
name: "Minimal",
|
|
17
|
+
description: "Quiet typography for concise technical writing.",
|
|
18
|
+
prompt: "Use minimal inline styles, high readability, compact headings, and no decorative clutter."
|
|
19
|
+
}
|
|
20
|
+
];
|
|
21
|
+
//# sourceMappingURL=builtin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtin.js","sourceRoot":"","sources":["../../../src/themes/builtin.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAsB;IAC9C;QACE,EAAE,EAAE,SAAS;QACb,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,wDAAwD;QACrE,MAAM,EAAE,2HAA2H;KACpI;IACD;QACE,EAAE,EAAE,WAAW;QACf,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,gDAAgD;QAC7D,MAAM,EAAE,wGAAwG;KACjH;IACD;QACE,EAAE,EAAE,SAAS;QACb,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,iDAAiD;QAC9D,MAAM,EAAE,2FAA2F;KACpG;CACF,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ThemeDefinition {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
prompt: string;
|
|
6
|
+
}
|
|
7
|
+
export interface LoadedTheme extends ThemeDefinition {
|
|
8
|
+
builtin: boolean;
|
|
9
|
+
path?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ThemeRegistryOptions {
|
|
12
|
+
registryPath?: string;
|
|
13
|
+
projectThemesDir?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function listThemes(options?: ThemeRegistryOptions): Promise<LoadedTheme[]>;
|
|
16
|
+
export declare function resolveTheme(id: string, options?: ThemeRegistryOptions): Promise<LoadedTheme>;
|
|
17
|
+
export declare function registerTheme(name: string, themePath: string, options?: ThemeRegistryOptions): Promise<LoadedTheme>;
|
|
18
|
+
export declare function removeTheme(name: string, options?: ThemeRegistryOptions): Promise<void>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { AppError } from "../core/errors.js";
|
|
4
|
+
import { builtinThemes } from "./builtin.js";
|
|
5
|
+
const DEFAULT_REGISTRY_PATH = ".md2wechat/themes/registry.json";
|
|
6
|
+
export async function listThemes(options = {}) {
|
|
7
|
+
const registry = await readRegistry(options);
|
|
8
|
+
const customThemes = await Promise.all(registry.themes.map(async (entry) => {
|
|
9
|
+
const theme = await loadThemeFile(entry.path, entry.id);
|
|
10
|
+
return { ...theme, builtin: false, path: entry.path };
|
|
11
|
+
}));
|
|
12
|
+
return [
|
|
13
|
+
...builtinThemes.map((theme) => ({ ...theme, builtin: true })),
|
|
14
|
+
...customThemes
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
export async function resolveTheme(id, options = {}) {
|
|
18
|
+
const theme = (await listThemes(options)).find((candidate) => candidate.id === id);
|
|
19
|
+
if (!theme) {
|
|
20
|
+
throw new AppError("THEME_NOT_FOUND", `Theme not found: ${id}`);
|
|
21
|
+
}
|
|
22
|
+
return theme;
|
|
23
|
+
}
|
|
24
|
+
export async function registerTheme(name, themePath, options = {}) {
|
|
25
|
+
const theme = await loadThemeFile(themePath, name);
|
|
26
|
+
if (isBuiltinTheme(theme.id)) {
|
|
27
|
+
throw new AppError("THEME_BUILTIN", `Cannot register built-in theme: ${theme.id}`);
|
|
28
|
+
}
|
|
29
|
+
const registry = await readRegistry(options);
|
|
30
|
+
const absolutePath = resolve(themePath);
|
|
31
|
+
const existingIndex = registry.themes.findIndex((entry) => entry.id === theme.id);
|
|
32
|
+
const entry = { id: theme.id, path: absolutePath };
|
|
33
|
+
if (existingIndex >= 0) {
|
|
34
|
+
registry.themes[existingIndex] = entry;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
registry.themes.push(entry);
|
|
38
|
+
}
|
|
39
|
+
await writeRegistry(registry, options);
|
|
40
|
+
return { ...theme, builtin: false, path: absolutePath };
|
|
41
|
+
}
|
|
42
|
+
export async function removeTheme(name, options = {}) {
|
|
43
|
+
if (isBuiltinTheme(name)) {
|
|
44
|
+
throw new AppError("THEME_BUILTIN", `Cannot remove built-in theme: ${name}`);
|
|
45
|
+
}
|
|
46
|
+
const registry = await readRegistry(options);
|
|
47
|
+
const nextThemes = registry.themes.filter((entry) => entry.id !== name);
|
|
48
|
+
await writeRegistry({ themes: nextThemes }, options);
|
|
49
|
+
}
|
|
50
|
+
async function loadThemeFile(themePath, suppliedId) {
|
|
51
|
+
let parsed;
|
|
52
|
+
try {
|
|
53
|
+
parsed = JSON.parse(await readFile(themePath, "utf8"));
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw new AppError("THEME_INVALID", `Unable to read theme file: ${themePath}`, false, error);
|
|
57
|
+
}
|
|
58
|
+
if (!isRecord(parsed)) {
|
|
59
|
+
throw new AppError("THEME_INVALID", "Theme file must contain a JSON object");
|
|
60
|
+
}
|
|
61
|
+
const id = stringValue(parsed.id) || suppliedId;
|
|
62
|
+
const name = stringValue(parsed.name);
|
|
63
|
+
const prompt = stringValue(parsed.prompt);
|
|
64
|
+
if (!id || !name || !prompt) {
|
|
65
|
+
throw new AppError("THEME_INVALID", "Theme id, name, and prompt must be non-empty strings");
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
id,
|
|
69
|
+
name,
|
|
70
|
+
description: stringValue(parsed.description),
|
|
71
|
+
prompt
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async function readRegistry(options) {
|
|
75
|
+
const registryPath = getRegistryPath(options);
|
|
76
|
+
try {
|
|
77
|
+
const parsed = JSON.parse(await readFile(registryPath, "utf8"));
|
|
78
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.themes)) {
|
|
79
|
+
throw new AppError("THEME_REGISTRY_INVALID", "Theme registry must contain a themes array");
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
themes: parsed.themes.map(parseRegistryEntry)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (isMissingFile(error)) {
|
|
87
|
+
return { themes: [] };
|
|
88
|
+
}
|
|
89
|
+
if (error instanceof AppError) {
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
throw new AppError("THEME_REGISTRY_INVALID", `Unable to read theme registry: ${registryPath}`, false, error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function writeRegistry(registry, options) {
|
|
96
|
+
const registryPath = getRegistryPath(options);
|
|
97
|
+
await mkdir(dirname(registryPath), { recursive: true });
|
|
98
|
+
await writeFile(registryPath, `${JSON.stringify(registry, null, 2)}\n`);
|
|
99
|
+
}
|
|
100
|
+
function getRegistryPath(options) {
|
|
101
|
+
return resolve(options.registryPath || options.projectThemesDir || DEFAULT_REGISTRY_PATH);
|
|
102
|
+
}
|
|
103
|
+
function parseRegistryEntry(value) {
|
|
104
|
+
if (!isRecord(value)) {
|
|
105
|
+
throw new AppError("THEME_REGISTRY_INVALID", "Theme registry entries must be JSON objects");
|
|
106
|
+
}
|
|
107
|
+
const id = stringValue(value.id);
|
|
108
|
+
const entryPath = stringValue(value.path);
|
|
109
|
+
if (!id || !entryPath) {
|
|
110
|
+
throw new AppError("THEME_REGISTRY_INVALID", "Theme registry entries require id and path");
|
|
111
|
+
}
|
|
112
|
+
return { id, path: entryPath };
|
|
113
|
+
}
|
|
114
|
+
function isBuiltinTheme(id) {
|
|
115
|
+
return builtinThemes.some((theme) => theme.id === id);
|
|
116
|
+
}
|
|
117
|
+
function isMissingFile(error) {
|
|
118
|
+
return isRecord(error) && error.code === "ENOENT";
|
|
119
|
+
}
|
|
120
|
+
function isRecord(value) {
|
|
121
|
+
return typeof value === "object" && value !== null;
|
|
122
|
+
}
|
|
123
|
+
function stringValue(value) {
|
|
124
|
+
if (typeof value !== "string") {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
const trimmed = value.trim();
|
|
128
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/themes/registry.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gangtiser/md2wechat",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript md2wechat CLI, Skill, and MCP server without hosted API conversion mode.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"wechat",
|
|
7
|
+
"markdown",
|
|
8
|
+
"mcp",
|
|
9
|
+
"codex",
|
|
10
|
+
"cli",
|
|
11
|
+
"openai"
|
|
12
|
+
],
|
|
13
|
+
"author": "Gangtiser",
|
|
14
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"bin": {
|
|
17
|
+
"md2wechat": "./dist/src/cli/index.js"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/gangtiser/md2wechat.git"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/gangtiser/md2wechat#readme",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/gangtiser/md2wechat/issues"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist/src",
|
|
29
|
+
"skills",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc",
|
|
35
|
+
"typecheck": "tsc --noEmit",
|
|
36
|
+
"test": "npm run build && node --test \"dist/test/**/*.test.js\"",
|
|
37
|
+
"verify:api-removal": "npm run build && node --test dist/test/api-removal.test.js",
|
|
38
|
+
"verify": "npm run typecheck && npm test && npm run verify:api-removal",
|
|
39
|
+
"prepack": "npm run build",
|
|
40
|
+
"prepublishOnly": "npm run typecheck && npm run build && npm run verify:api-removal"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@modelcontextprotocol/sdk": "0.6.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^24.3.0",
|
|
50
|
+
"typescript": "^5.9.2"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: md2wechat
|
|
3
|
+
description: Convert Markdown to WeChat Official Account HTML, generate images, manage themes, and use the md2wechat MCP server.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# md2wechat Skill
|
|
7
|
+
|
|
8
|
+
Use this skill when the user wants to turn Markdown into WeChat Official Account content, preview an article, generate a cover or infographic, manage themes, or expose the workflow through MCP.
|
|
9
|
+
|
|
10
|
+
## Defaults
|
|
11
|
+
|
|
12
|
+
- Text rendering model: `gpt-5.5`
|
|
13
|
+
- Image generation model: `gpt-image-2`
|
|
14
|
+
- Conversion is local through the CLI or MCP server.
|
|
15
|
+
|
|
16
|
+
## Workflow
|
|
17
|
+
|
|
18
|
+
1. Run `md2wechat inspect <file> --json`.
|
|
19
|
+
2. Run `md2wechat themes list --json` before choosing a theme.
|
|
20
|
+
3. Run `md2wechat convert <file> --theme <theme> --output <file>.html --json`.
|
|
21
|
+
4. Run `md2wechat preview <file> --theme <theme> --output preview.html --json` when the user wants a local confirmation page.
|
|
22
|
+
5. Run `md2wechat generate-image --article <file> --output cover.png --json` for covers or visual assets.
|
|
23
|
+
|
|
24
|
+
## Theme Management
|
|
25
|
+
|
|
26
|
+
- Register a theme: `md2wechat themes register <name> <path> --json`
|
|
27
|
+
- Remove a custom theme: `md2wechat themes remove <name> --json`
|
|
28
|
+
- List themes: `md2wechat themes list --json`
|
|
29
|
+
|
|
30
|
+
## MCP
|
|
31
|
+
|
|
32
|
+
Start the MCP server with:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
md2wechat mcp
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Available tools:
|
|
39
|
+
|
|
40
|
+
- `convert_article`
|
|
41
|
+
- `preview_article`
|
|
42
|
+
- `generate_image`
|
|
43
|
+
- `list_themes`
|
|
44
|
+
- `register_theme`
|
|
45
|
+
- `remove_theme`
|
|
46
|
+
|
|
47
|
+
## Rules
|
|
48
|
+
|
|
49
|
+
- Prefer file paths over large inline article content.
|
|
50
|
+
- Do not assume a theme exists; list themes first.
|
|
51
|
+
- Keep generated HTML and preview files on disk unless the user asks for stdout.
|
|
52
|
+
- Treat `OPENAI_API_KEY` as required for conversion and image generation.
|