create-linkdesk-plugin 0.1.1 → 0.1.2

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Encaron
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,55 +1,55 @@
1
- # create-linkdesk-plugin
2
-
3
- LinkDesk 插件脚手架——一行命令生成你的第一个插件项目(对标 `yo code`)。
4
-
5
- ```bash
6
- npm create linkdesk-plugin my-cool-plugin
7
- ```
8
-
9
- 不带名字则交互式询问:
10
-
11
- ```bash
12
- npm create linkdesk-plugin
13
- ```
14
-
15
- ## 生成什么
16
-
17
- 生成物的形状**与官方插件一致**——README / CHANGELOG / resources / i18n 一个不少:
18
-
19
- ```
20
- my-cool-plugin/
21
- ├── plugin.json # 插件清单(JSONC:可注释/尾逗号,字段分节示范,VS Code $schema 校验)
22
- ├── package.json # scripts: dev / dev:real / build / publish / validate / lint
23
- ├── tsconfig.json # jsx: react-jsx + window.linkdesk.* 类型(@linkdesk/plugin-sdk)
24
- ├── .gitignore # node_modules / dist / *.linkdesk-plugin
25
- ├── README.md # 说明——市场「详情」页签的数据源 + 目录契约表
26
- ├── CHANGELOG.md # 更新日志——市场「更改日志」页签的数据源
27
- ├── .vscode/settings.json # plugin.json 按 jsonc 识别(注释不标红)
28
- ├── resources/
29
- │ └── icon.svg # 图标占位图——换成你自己的
30
- ├── src/
31
- │ ├── index.tsx # 视图组件 default 导出——壳以 { isActive, tabId?, sourceId? } 渲染
32
- │ └── index.css # 样式示例——颜色 / 字号走 var(--xxx),间距走 4px 节奏
33
- └── i18n/
34
- └── en.json # 英文译文(key = 中文原文;不建 zh.json)
35
- ```
36
-
37
- > **不预建空文件夹**(git 本来也不记录空目录)——「东西该放哪」写在生成的 `README.md` 的目录契约表里,
38
- > 用文字说清比用空夹暗示更清楚。
39
-
40
- ## 命令
41
-
42
- ```bash
43
- cd my-cool-plugin
44
- npm install
45
-
46
- npm run dev # 浏览器热重载预览(改代码即时生效)
47
- npm run dev:real # 真机环——直写 {userData}/plugins/<id> + CDP reload(真 IPC / 串口 / LSP 类插件用)
48
- npm run validate # 校验 plugin.json
49
- npm run build # 产出 <pluginId>.linkdesk-plugin——可装进 LinkDesk / 发布
50
- npm run lint # 门禁自检(硬编码颜色 / 字号 / 间距网格 / eslint 规则)
51
- npm run publish # 一键发布(建 GitHub Release + 上传 + 更新 catalog)
52
- ```
53
-
54
- > 说明:插件作者工作流(dev 热预览 / build / 发布全链路)的完整文档见
55
- > [00-第三方作者旅程](../../docs/02-Electron架构/E6_插件生态与发布/05-文档与发布/00-第三方作者旅程.md)。
1
+ # create-linkdesk-plugin
2
+
3
+ LinkDesk 插件脚手架——一行命令生成你的第一个插件项目(对标 `yo code`)。
4
+
5
+ ```bash
6
+ npm create linkdesk-plugin my-cool-plugin
7
+ ```
8
+
9
+ 不带名字则交互式询问:
10
+
11
+ ```bash
12
+ npm create linkdesk-plugin
13
+ ```
14
+
15
+ ## 生成什么
16
+
17
+ 生成物的形状**与官方插件一致**——README / CHANGELOG / resources / i18n 一个不少:
18
+
19
+ ```
20
+ my-cool-plugin/
21
+ ├── plugin.json # 插件清单(JSONC:可注释/尾逗号,字段分节示范,VS Code $schema 校验)
22
+ ├── package.json # scripts: dev / dev:real / build / publish / validate / lint
23
+ ├── tsconfig.json # jsx: react-jsx + window.linkdesk.* 类型(@linkdesk/plugin-sdk)
24
+ ├── .gitignore # node_modules / dist / *.linkdesk-plugin
25
+ ├── README.md # 说明——市场「详情」页签的数据源 + 目录契约表
26
+ ├── CHANGELOG.md # 更新日志——市场「更改日志」页签的数据源
27
+ ├── .vscode/settings.json # plugin.json 按 jsonc 识别(注释不标红)
28
+ ├── resources/
29
+ │ └── icon.svg # 图标占位图——换成你自己的
30
+ ├── src/
31
+ │ ├── index.tsx # 视图组件 default 导出——壳以 { isActive, tabId?, sourceId? } 渲染
32
+ │ └── index.css # 样式示例——颜色 / 字号走 var(--xxx),间距走 4px 节奏
33
+ └── i18n/
34
+ └── en.json # 英文译文(key = 中文原文;不建 zh.json)
35
+ ```
36
+
37
+ > **不预建空文件夹**(git 本来也不记录空目录)——「东西该放哪」写在生成的 `README.md` 的目录契约表里,
38
+ > 用文字说清比用空夹暗示更清楚。
39
+
40
+ ## 命令
41
+
42
+ ```bash
43
+ cd my-cool-plugin
44
+ npm install
45
+
46
+ npm run dev # 浏览器热重载预览(改代码即时生效)
47
+ npm run dev:real # 真机环——直写 {userData}/plugins/<id> + CDP reload(真 IPC / 串口 / LSP 类插件用)
48
+ npm run validate # 校验 plugin.json
49
+ npm run build # 产出 <pluginId>.linkdesk-plugin——可装进 LinkDesk / 发布
50
+ npm run lint # 门禁自检(硬编码颜色 / 字号 / 间距网格 / eslint 规则)
51
+ npm run publish # 一键发布(建 GitHub Release + 上传 + 更新 catalog)
52
+ ```
53
+
54
+ > 说明:插件作者工作流(dev 热预览 / build / 发布全链路)的完整文档见
55
+ > [00-第三方作者旅程](../../docs/02-Electron架构/E6_插件生态与发布/05-文档与发布/00-第三方作者旅程.md)。
package/index.js CHANGED
@@ -1,136 +1,136 @@
1
- #!/usr/bin/env node
2
- /**
3
- * create-linkdesk-plugin——LinkDesk 插件脚手架 CLI(纯 Node,零依赖,对标 yo code)。
4
- *
5
- * 用法:
6
- * npm create linkdesk-plugin my-cool-plugin # 直接给名字(kebab-case,非交互)
7
- * npm create linkdesk-plugin # 不带参数 → 交互式询问插件名
8
- *
9
- * 行为:把同目录 template/ 复制到 <cwd>/<name>,把占位符替换成真实值,打印下一步提示。
10
- * 占位符:{{pluginName}} {{displayName}} {{author}} {{date}}(递归替换所有模板文件)。
11
- * {{date}} 注入 CHANGELOG.md 的初始段标题——格式必须是 `## v<版本>(YYYY-MM-DD)`,
12
- * 那是市场「更改日志」页签切段的解析依据(见 docs/02-Electron架构/.../插件规范化层/02)。
13
- *
14
- * 生成产物契约:见 docs/02-Electron架构/E6_插件生态与发布/02-插件开发工具链/01-create-linkdesk-plugin脚手架.md。
15
- */
16
- import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
17
- import { spawnSync } from "node:child_process";
18
- import { createInterface } from "node:readline";
19
- import { dirname, join } from "node:path";
20
- import { fileURLToPath } from "node:url";
21
-
22
- const TEMPLATE_DIR = join(dirname(fileURLToPath(import.meta.url)), "template");
23
-
24
- /** kebab-case——同时满足插件 id / viewsContainers key / npm 包名惯例(SAFE_PLUGIN_ID 的形状子集) */
25
- const NAME_RE = /^[a-z][a-z0-9-]*$/;
26
-
27
- /** 本地日期 YYYY-MM-DD(不用 toISOString——那是 UTC,跨时区会差一天) */
28
- function todayLocal() {
29
- const d = new Date();
30
- const p = (n) => String(n).padStart(2, "0");
31
- return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
32
- }
33
-
34
- /** my-cool-plugin → My Cool Plugin */
35
- function toDisplayName(name) {
36
- return name
37
- .split("-")
38
- .map((s) => (s ? s[0].toUpperCase() + s.slice(1) : s))
39
- .join(" ");
40
- }
41
-
42
- /** 作者默认值 = git config user.name;读不到(无 git/无配置)→ "you"(作者生成后自改) */
43
- function gitUserName() {
44
- try {
45
- const r = spawnSync("git", ["config", "user.name"], { encoding: "utf8", timeout: 3000 });
46
- const v = (r.stdout || "").trim();
47
- return v || "you";
48
- } catch {
49
- return "you";
50
- }
51
- }
52
-
53
- /** 交互式单问——返回去除首尾空白的答案 */
54
- function ask(question) {
55
- const rl = createInterface({ input: process.stdin, output: process.stdout });
56
- return new Promise((resolve) => {
57
- rl.question(question, (answer) => {
58
- rl.close();
59
- resolve(answer.trim());
60
- });
61
- });
62
- }
63
-
64
- function replacePlaceholders(file, values) {
65
- let text = readFileSync(file, "utf8");
66
- for (const [key, value] of Object.entries(values)) {
67
- text = text.split(`{{${key}}}`).join(value);
68
- }
69
- writeFileSync(file, text);
70
- }
71
-
72
- /** 递归替换目录内全部文件(模板全是文本文件,无需跳过二进制) */
73
- function walkReplace(dir, values) {
74
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
75
- const full = join(dir, entry.name);
76
- if (entry.isDirectory()) walkReplace(full, values);
77
- else replacePlaceholders(full, values);
78
- }
79
- }
80
-
81
- async function main() {
82
- let name = (process.argv[2] || "").trim();
83
- if (!name) {
84
- name = await ask("插件名(kebab-case,如 my-cool-plugin): ");
85
- }
86
- name = name.trim();
87
- if (!NAME_RE.test(name)) {
88
- console.error(`✖ 插件名须为 kebab-case(小写字母/数字/连字符),收到:${JSON.stringify(name)}`);
89
- process.exit(1);
90
- }
91
-
92
- const target = join(process.cwd(), name);
93
- if (existsSync(target) && readdirSync(target).length > 0) {
94
- console.error(`✖ ${name}/ 已存在且非空——换个名字,或清空后重跑`);
95
- process.exit(1);
96
- }
97
-
98
- mkdirSync(target, { recursive: true });
99
- cpSync(TEMPLATE_DIR, target, { recursive: true });
100
-
101
- // 🔥 模板里存的是 `gitignore`(无点),生成时才改名为 `.gitignore`。
102
- // 原因:**npm 打包恒定丢弃名为 `.gitignore` 的文件**(npm-packlist 排除表;实测
103
- // `template/.gitignoreprobe` 与 `template/probe.txt` 都能进 tarball,唯独 `.gitignore` 不能)。
104
- // 若模板里直接放 `.gitignore`,仓内生成(读模板目录)一切正常,**但发布后的
105
- // `npm create linkdesk-plugin` 生成的工程会没有 .gitignore**——作者第一次 `git add .`
106
- // 就把 node_modules/ 和 dist/ 全提交了。生成物契约见 check-scaffold.mjs 断言 8。
107
- const tplGitignore = join(target, "gitignore");
108
- if (existsSync(tplGitignore)) renameSync(tplGitignore, join(target, ".gitignore"));
109
-
110
- const values = {
111
- pluginName: name,
112
- displayName: toDisplayName(name),
113
- author: gitUserName(),
114
- date: todayLocal(),
115
- };
116
- walkReplace(target, values);
117
-
118
- console.log("");
119
- console.log(`✔ ${name}/ 已创建`);
120
- console.log("");
121
- console.log(" 接下来:");
122
- console.log(` cd ${name}`);
123
- console.log(" npm install");
124
- console.log(" npm run dev # 浏览器热重载预览(改代码即时生效)");
125
- console.log(" npm run validate # 校验 plugin.json($schema / 字段 / i18n 文件)");
126
- console.log(" npm run build # 打包出 <pluginId>.linkdesk-plugin,可装进 LinkDesk / 发布");
127
- console.log("");
128
- console.log(" 然后:先读 README.md —— 目录该放哪、三条纪律、怎么发布都在里面。");
129
- console.log(" plugin.json 的 name / description / author 是你的身份信息,src/index.tsx 是插件本体。");
130
- console.log(" 完整插件能力(侧栏视图 / 命令 / 设置 / 协议……)见 docs/03-插件制造/。");
131
- }
132
-
133
- main().catch((err) => {
134
- console.error(err);
135
- process.exit(1);
136
- });
1
+ #!/usr/bin/env node
2
+ /**
3
+ * create-linkdesk-plugin——LinkDesk 插件脚手架 CLI(纯 Node,零依赖,对标 yo code)。
4
+ *
5
+ * 用法:
6
+ * npm create linkdesk-plugin my-cool-plugin # 直接给名字(kebab-case,非交互)
7
+ * npm create linkdesk-plugin # 不带参数 → 交互式询问插件名
8
+ *
9
+ * 行为:把同目录 template/ 复制到 <cwd>/<name>,把占位符替换成真实值,打印下一步提示。
10
+ * 占位符:{{pluginName}} {{displayName}} {{author}} {{date}}(递归替换所有模板文件)。
11
+ * {{date}} 注入 CHANGELOG.md 的初始段标题——格式必须是 `## v<版本>(YYYY-MM-DD)`,
12
+ * 那是市场「更改日志」页签切段的解析依据(见 docs/02-Electron架构/.../插件规范化层/02)。
13
+ *
14
+ * 生成产物契约:见 docs/02-Electron架构/E6_插件生态与发布/02-插件开发工具链/01-create-linkdesk-plugin脚手架.md。
15
+ */
16
+ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
17
+ import { spawnSync } from "node:child_process";
18
+ import { createInterface } from "node:readline";
19
+ import { dirname, join } from "node:path";
20
+ import { fileURLToPath } from "node:url";
21
+
22
+ const TEMPLATE_DIR = join(dirname(fileURLToPath(import.meta.url)), "template");
23
+
24
+ /** kebab-case——同时满足插件 id / viewsContainers key / npm 包名惯例(SAFE_PLUGIN_ID 的形状子集) */
25
+ const NAME_RE = /^[a-z][a-z0-9-]*$/;
26
+
27
+ /** 本地日期 YYYY-MM-DD(不用 toISOString——那是 UTC,跨时区会差一天) */
28
+ function todayLocal() {
29
+ const d = new Date();
30
+ const p = (n) => String(n).padStart(2, "0");
31
+ return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
32
+ }
33
+
34
+ /** my-cool-plugin → My Cool Plugin */
35
+ function toDisplayName(name) {
36
+ return name
37
+ .split("-")
38
+ .map((s) => (s ? s[0].toUpperCase() + s.slice(1) : s))
39
+ .join(" ");
40
+ }
41
+
42
+ /** 作者默认值 = git config user.name;读不到(无 git/无配置)→ "you"(作者生成后自改) */
43
+ function gitUserName() {
44
+ try {
45
+ const r = spawnSync("git", ["config", "user.name"], { encoding: "utf8", timeout: 3000 });
46
+ const v = (r.stdout || "").trim();
47
+ return v || "you";
48
+ } catch {
49
+ return "you";
50
+ }
51
+ }
52
+
53
+ /** 交互式单问——返回去除首尾空白的答案 */
54
+ function ask(question) {
55
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
56
+ return new Promise((resolve) => {
57
+ rl.question(question, (answer) => {
58
+ rl.close();
59
+ resolve(answer.trim());
60
+ });
61
+ });
62
+ }
63
+
64
+ function replacePlaceholders(file, values) {
65
+ let text = readFileSync(file, "utf8");
66
+ for (const [key, value] of Object.entries(values)) {
67
+ text = text.split(`{{${key}}}`).join(value);
68
+ }
69
+ writeFileSync(file, text);
70
+ }
71
+
72
+ /** 递归替换目录内全部文件(模板全是文本文件,无需跳过二进制) */
73
+ function walkReplace(dir, values) {
74
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
75
+ const full = join(dir, entry.name);
76
+ if (entry.isDirectory()) walkReplace(full, values);
77
+ else replacePlaceholders(full, values);
78
+ }
79
+ }
80
+
81
+ async function main() {
82
+ let name = (process.argv[2] || "").trim();
83
+ if (!name) {
84
+ name = await ask("插件名(kebab-case,如 my-cool-plugin): ");
85
+ }
86
+ name = name.trim();
87
+ if (!NAME_RE.test(name)) {
88
+ console.error(`✖ 插件名须为 kebab-case(小写字母/数字/连字符),收到:${JSON.stringify(name)}`);
89
+ process.exit(1);
90
+ }
91
+
92
+ const target = join(process.cwd(), name);
93
+ if (existsSync(target) && readdirSync(target).length > 0) {
94
+ console.error(`✖ ${name}/ 已存在且非空——换个名字,或清空后重跑`);
95
+ process.exit(1);
96
+ }
97
+
98
+ mkdirSync(target, { recursive: true });
99
+ cpSync(TEMPLATE_DIR, target, { recursive: true });
100
+
101
+ // 🔥 模板里存的是 `gitignore`(无点),生成时才改名为 `.gitignore`。
102
+ // 原因:**npm 打包恒定丢弃名为 `.gitignore` 的文件**(npm-packlist 排除表;实测
103
+ // `template/.gitignoreprobe` 与 `template/probe.txt` 都能进 tarball,唯独 `.gitignore` 不能)。
104
+ // 若模板里直接放 `.gitignore`,仓内生成(读模板目录)一切正常,**但发布后的
105
+ // `npm create linkdesk-plugin` 生成的工程会没有 .gitignore**——作者第一次 `git add .`
106
+ // 就把 node_modules/ 和 dist/ 全提交了。生成物契约见 check-scaffold.mjs 断言 8。
107
+ const tplGitignore = join(target, "gitignore");
108
+ if (existsSync(tplGitignore)) renameSync(tplGitignore, join(target, ".gitignore"));
109
+
110
+ const values = {
111
+ pluginName: name,
112
+ displayName: toDisplayName(name),
113
+ author: gitUserName(),
114
+ date: todayLocal(),
115
+ };
116
+ walkReplace(target, values);
117
+
118
+ console.log("");
119
+ console.log(`✔ ${name}/ 已创建`);
120
+ console.log("");
121
+ console.log(" 接下来:");
122
+ console.log(` cd ${name}`);
123
+ console.log(" npm install");
124
+ console.log(" npm run dev # 浏览器热重载预览(改代码即时生效)");
125
+ console.log(" npm run validate # 校验 plugin.json($schema / 字段 / i18n 文件)");
126
+ console.log(" npm run build # 打包出 <pluginId>.linkdesk-plugin,可装进 LinkDesk / 发布");
127
+ console.log("");
128
+ console.log(" 然后:先读 README.md —— 目录该放哪、三条纪律、怎么发布都在里面。");
129
+ console.log(" plugin.json 的 name / description / author 是你的身份信息,src/index.tsx 是插件本体。");
130
+ console.log(" 完整插件能力(侧栏视图 / 命令 / 设置 / 协议……)见 docs/03-插件制造/。");
131
+ }
132
+
133
+ main().catch((err) => {
134
+ console.error(err);
135
+ process.exit(1);
136
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-linkdesk-plugin",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "LinkDesk 插件脚手架——`npm create linkdesk-plugin my-cool-plugin` 一行生成你的第一个插件项目(对标 yo code)。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,8 +1,8 @@
1
- {
2
- // 让 VS Code 按 JSONC 解析 plugin.json —— 它允许 // 注释与尾逗号(对标 VS Code 自己的 package.json)。
3
- // 没有这一行,VS Code 会按严格 JSON 解析,本模板 plugin.json 里的注释会被标成满屏红波浪线。
4
- // 这只是编辑器提示,不影响运行:LinkDesk 与 SDK 本来就按 jsonc 解析。
5
- "files.associations": {
6
- "plugin.json": "jsonc"
7
- }
8
- }
1
+ {
2
+ // 让 VS Code 按 JSONC 解析 plugin.json —— 它允许 // 注释与尾逗号(对标 VS Code 自己的 package.json)。
3
+ // 没有这一行,VS Code 会按严格 JSON 解析,本模板 plugin.json 里的注释会被标成满屏红波浪线。
4
+ // 这只是编辑器提示,不影响运行:LinkDesk 与 SDK 本来就按 jsonc 解析。
5
+ "files.associations": {
6
+ "plugin.json": "jsonc"
7
+ }
8
+ }
@@ -1,8 +1,8 @@
1
- # 依赖
2
- node_modules/
3
-
4
- # 构建中间产物(可随时 npm run build 重建)
5
- dist/
6
-
7
- # 分发包——由 GitHub Release 承载,不进你的源码仓
8
- *.linkdesk-plugin
1
+ # 依赖
2
+ node_modules/
3
+
4
+ # 构建中间产物(可随时 npm run build 重建)
5
+ dist/
6
+
7
+ # 分发包——由 GitHub Release 承载,不进你的源码仓
8
+ *.linkdesk-plugin
@@ -1,23 +1,23 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48">
2
- <!-- ⚠️ 占位图——请替换成你自己插件的图标(同名同位置覆盖即可,plugin.json 的 icon 已指向本文件)。
3
- 写法与官方插件一致:界面里以 <img src="linkdesk://<id>/resources/icon.svg"> 显示,
4
- 是独立文档 ⇒ 不能用 var(--xxx)(解析不到),颜色必须自含实色。 -->
5
- <defs>
6
- <linearGradient id="phi_bg" x1="0" y1="0" x2="1" y2="1">
7
- <stop offset="0" stop-color="#64748B"/>
8
- <stop offset="1" stop-color="#334155"/>
9
- </linearGradient>
10
- <linearGradient id="phi_glass" x1="0" y1="0" x2="0" y2="1">
11
- <stop offset="0" stop-color="#FFFFFF" stop-opacity="0.28"/>
12
- <stop offset="1" stop-color="#FFFFFF" stop-opacity="0.02"/>
13
- </linearGradient>
14
- </defs>
15
- <!-- 中性灰品牌块:刻意不设计成有含义的图形——它的意思是「这里还没有图」 -->
16
- <rect x="0" y="0" width="48" height="48" rx="10" fill="url(#phi_bg)"/>
17
- <rect x="0" y="0" width="48" height="48" rx="10" fill="url(#phi_glass)"/>
18
- <!-- 虚线框 + 加号 = 「把你的图放这儿」 -->
19
- <rect x="13" y="13" width="22" height="22" rx="5" fill="none"
20
- stroke="#FFFFFF" stroke-opacity="0.9" stroke-width="2"
21
- stroke-dasharray="4 3" stroke-linecap="round"/>
22
- <path d="M24 19.5v9M19.5 24h9" stroke="#FFFFFF" stroke-width="2.4" stroke-linecap="round"/>
23
- </svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48">
2
+ <!-- ⚠️ 占位图——请替换成你自己插件的图标(同名同位置覆盖即可,plugin.json 的 icon 已指向本文件)。
3
+ 写法与官方插件一致:界面里以 <img src="linkdesk://<id>/resources/icon.svg"> 显示,
4
+ 是独立文档 ⇒ 不能用 var(--xxx)(解析不到),颜色必须自含实色。 -->
5
+ <defs>
6
+ <linearGradient id="phi_bg" x1="0" y1="0" x2="1" y2="1">
7
+ <stop offset="0" stop-color="#64748B"/>
8
+ <stop offset="1" stop-color="#334155"/>
9
+ </linearGradient>
10
+ <linearGradient id="phi_glass" x1="0" y1="0" x2="0" y2="1">
11
+ <stop offset="0" stop-color="#FFFFFF" stop-opacity="0.28"/>
12
+ <stop offset="1" stop-color="#FFFFFF" stop-opacity="0.02"/>
13
+ </linearGradient>
14
+ </defs>
15
+ <!-- 中性灰品牌块:刻意不设计成有含义的图形——它的意思是「这里还没有图」 -->
16
+ <rect x="0" y="0" width="48" height="48" rx="10" fill="url(#phi_bg)"/>
17
+ <rect x="0" y="0" width="48" height="48" rx="10" fill="url(#phi_glass)"/>
18
+ <!-- 虚线框 + 加号 = 「把你的图放这儿」 -->
19
+ <rect x="13" y="13" width="22" height="22" rx="5" fill="none"
20
+ stroke="#FFFFFF" stroke-opacity="0.9" stroke-width="2"
21
+ stroke-dasharray="4 3" stroke-linecap="round"/>
22
+ <path d="M24 19.5v9M19.5 24h9" stroke="#FFFFFF" stroke-width="2.4" stroke-linecap="round"/>
23
+ </svg>
@@ -1,30 +1,30 @@
1
- /* {{displayName}} 样式示例(`npm run lint` 会照着下面三条查)。
2
- 三条纪律:
3
- · 颜色一律 var(--xxx),禁硬编码 hex——用户换主题时你的插件要跟着变;
4
- · 字号一律 var(--font-size-*)——用户调全局字号时它才跟着缩放,裸 px 不会;
5
- · padding / margin / gap 走 4px 节奏(4 的倍数)。 */
6
-
7
- .starter {
8
- padding: 24px;
9
- font-family: inherit;
10
- }
11
-
12
- .starter__title {
13
- margin: 0 0 8px;
14
- color: var(--text);
15
- }
16
-
17
- .starter__text {
18
- margin: 0 0 4px;
19
- color: var(--text-muted);
20
- }
21
-
22
- .starter__hint {
23
- margin: 0;
24
- color: var(--text-muted);
25
- font-size: var(--font-size-xs);
26
- }
27
-
28
- .starter__hint code {
29
- font-family: var(--font-mono, monospace);
30
- }
1
+ /* {{displayName}} 样式示例(`npm run lint` 会照着下面三条查)。
2
+ 三条纪律:
3
+ · 颜色一律 var(--xxx),禁硬编码 hex——用户换主题时你的插件要跟着变;
4
+ · 字号一律 var(--font-size-*)——用户调全局字号时它才跟着缩放,裸 px 不会;
5
+ · padding / margin / gap 走 4px 节奏(4 的倍数)。 */
6
+
7
+ .starter {
8
+ padding: 24px;
9
+ font-family: inherit;
10
+ }
11
+
12
+ .starter__title {
13
+ margin: 0 0 8px;
14
+ color: var(--text);
15
+ }
16
+
17
+ .starter__text {
18
+ margin: 0 0 4px;
19
+ color: var(--text-muted);
20
+ }
21
+
22
+ .starter__hint {
23
+ margin: 0;
24
+ color: var(--text-muted);
25
+ font-size: var(--font-size-xs);
26
+ }
27
+
28
+ .starter__hint code {
29
+ font-family: var(--font-mono, monospace);
30
+ }
@@ -1,35 +1,35 @@
1
- /**
2
- * {{displayName}}——LinkDesk 插件主视图(由 create-linkdesk-plugin 生成)。
3
- *
4
- * 视图插件契约(E5.8,见 docs/03-插件制造/01-API契约.md):壳以 { isActive, tabId?, sourceId? }
5
- * 渲染本文件 default 导出的组件:
6
- * - isActive 本标签当前是否聚焦。keep-alive 下非聚焦标签仍在渲染,isActive 只用于
7
- * gate「聚焦才跑」的副作用(如自动保存),切勿用它整块 blank 掉内容。
8
- * - tabId 本标签页 id。
9
- * - sourceId 上下文数据(文件路径 / 数据源等),编辑器类插件用它定位内容。
10
- *
11
- * 样式:LinkDesk 主题色一律走 CSS 变量 var(--xxx)(见 index.css 示例),禁硬编码 hex。
12
- * 文案:用 t() 读——key 就是中文原文,英文译文放 i18n/en.json(见 05-UI写法规约.md)。
13
- * 壳已 external react/react-dom/react-i18next/i18next——构建不会打进包,插件工程无需 npm i 它们。
14
- */
15
-
16
- import { useTranslation } from "react-i18next";
17
- import "./index.css";
18
-
19
- export default function HelloPlugin(_props: { isActive?: boolean; tabId?: string; sourceId?: string }) {
20
- const { t } = useTranslation();
21
-
22
- return (
23
- <div className="starter">
24
- <h2 className="starter__title">{t("插件跑起来了 ✨")}</h2>
25
- <p className="starter__text">{t("这是你的第一个 LinkDesk 插件。")}</p>
26
- <p className="starter__hint">
27
- <code>src/index.tsx</code> {t("是插件本体——改它,浏览器预览即时刷新。")}
28
- </p>
29
- <p className="starter__hint">
30
- <code>npm run build</code> {t("打包出分发文件,可装进 LinkDesk 或发布到市场。")}
31
- </p>
32
- <p className="starter__hint">{t("目录该放哪、发布怎么做,都写在 README.md 里。")}</p>
33
- </div>
34
- );
35
- }
1
+ /**
2
+ * {{displayName}}——LinkDesk 插件主视图(由 create-linkdesk-plugin 生成)。
3
+ *
4
+ * 视图插件契约(E5.8,见 docs/03-插件制造/01-API契约.md):壳以 { isActive, tabId?, sourceId? }
5
+ * 渲染本文件 default 导出的组件:
6
+ * - isActive 本标签当前是否聚焦。keep-alive 下非聚焦标签仍在渲染,isActive 只用于
7
+ * gate「聚焦才跑」的副作用(如自动保存),切勿用它整块 blank 掉内容。
8
+ * - tabId 本标签页 id。
9
+ * - sourceId 上下文数据(文件路径 / 数据源等),编辑器类插件用它定位内容。
10
+ *
11
+ * 样式:LinkDesk 主题色一律走 CSS 变量 var(--xxx)(见 index.css 示例),禁硬编码 hex。
12
+ * 文案:用 t() 读——key 就是中文原文,英文译文放 i18n/en.json(见 05-UI写法规约.md)。
13
+ * 壳已 external react/react-dom/react-i18next/i18next——构建不会打进包,插件工程无需 npm i 它们。
14
+ */
15
+
16
+ import { useTranslation } from "react-i18next";
17
+ import "./index.css";
18
+
19
+ export default function HelloPlugin(_props: { isActive?: boolean; tabId?: string; sourceId?: string }) {
20
+ const { t } = useTranslation();
21
+
22
+ return (
23
+ <div className="starter">
24
+ <h2 className="starter__title">{t("插件跑起来了 ✨")}</h2>
25
+ <p className="starter__text">{t("这是你的第一个 LinkDesk 插件。")}</p>
26
+ <p className="starter__hint">
27
+ <code>src/index.tsx</code> {t("是插件本体——改它,浏览器预览即时刷新。")}
28
+ </p>
29
+ <p className="starter__hint">
30
+ <code>npm run build</code> {t("打包出分发文件,可装进 LinkDesk 或发布到市场。")}
31
+ </p>
32
+ <p className="starter__hint">{t("目录该放哪、发布怎么做,都写在 README.md 里。")}</p>
33
+ </div>
34
+ );
35
+ }
@@ -1,14 +1,14 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "strict": true,
7
- "noEmit": true,
8
- "jsx": "react-jsx",
9
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
10
- "types": ["@linkdesk/plugin-sdk"],
11
- "skipLibCheck": true
12
- },
13
- "include": ["src"]
14
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "noEmit": true,
8
+ "jsx": "react-jsx",
9
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
10
+ "types": ["@linkdesk/plugin-sdk"],
11
+ "skipLibCheck": true
12
+ },
13
+ "include": ["src"]
14
+ }