@lovision/plugin-dev 1.0.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 (96) hide show
  1. package/README.md +49 -0
  2. package/dist/build.d.ts +16 -0
  3. package/dist/build.d.ts.map +1 -0
  4. package/dist/build.js +108 -0
  5. package/dist/build.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +123 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/create-plugin.d.ts +19 -0
  11. package/dist/create-plugin.d.ts.map +1 -0
  12. package/dist/create-plugin.js +186 -0
  13. package/dist/create-plugin.js.map +1 -0
  14. package/dist/dev.d.ts +13 -0
  15. package/dist/dev.d.ts.map +1 -0
  16. package/dist/dev.js +206 -0
  17. package/dist/dev.js.map +1 -0
  18. package/dist/index.d.ts +13 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +7 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/publish.d.ts +15 -0
  23. package/dist/publish.d.ts.map +1 -0
  24. package/dist/publish.js +55 -0
  25. package/dist/publish.js.map +1 -0
  26. package/dist/shared.d.ts +93 -0
  27. package/dist/shared.d.ts.map +1 -0
  28. package/dist/shared.js +436 -0
  29. package/dist/shared.js.map +1 -0
  30. package/dist/templates/ai-layout-assistant/README.md.template +24 -0
  31. package/dist/templates/ai-layout-assistant/eslint.config.mjs +19 -0
  32. package/dist/templates/ai-layout-assistant/manifest.json.template +38 -0
  33. package/dist/templates/ai-layout-assistant/package.json.template +18 -0
  34. package/dist/templates/ai-layout-assistant/src/main.ts.template +345 -0
  35. package/dist/templates/ai-layout-assistant/tsconfig.json +14 -0
  36. package/dist/templates/ai-layout-assistant/ui.html.template +114 -0
  37. package/dist/templates/asset-browser/README.md.template +24 -0
  38. package/dist/templates/asset-browser/eslint.config.mjs +19 -0
  39. package/dist/templates/asset-browser/manifest.json.template +29 -0
  40. package/dist/templates/asset-browser/package.json.template +18 -0
  41. package/dist/templates/asset-browser/src/main.ts.template +177 -0
  42. package/dist/templates/asset-browser/tsconfig.json +14 -0
  43. package/dist/templates/asset-browser/ui.html.template +137 -0
  44. package/dist/templates/base/README.md.template +34 -0
  45. package/dist/templates/base/eslint.config.mjs +19 -0
  46. package/dist/templates/base/manifest.json.template +22 -0
  47. package/dist/templates/base/package.json.template +18 -0
  48. package/dist/templates/base/src/main.ts.template +20 -0
  49. package/dist/templates/base/tsconfig.json +14 -0
  50. package/dist/templates/batch-layout-organizer/README.md.template +24 -0
  51. package/dist/templates/batch-layout-organizer/eslint.config.mjs +19 -0
  52. package/dist/templates/batch-layout-organizer/manifest.json.template +31 -0
  53. package/dist/templates/batch-layout-organizer/package.json.template +18 -0
  54. package/dist/templates/batch-layout-organizer/src/main.ts.template +324 -0
  55. package/dist/templates/batch-layout-organizer/tsconfig.json +14 -0
  56. package/dist/templates/batch-layout-organizer/ui.html.template +116 -0
  57. package/dist/templates/data-filler-full/README.md.template +32 -0
  58. package/dist/templates/data-filler-full/eslint.config.mjs +19 -0
  59. package/dist/templates/data-filler-full/manifest.json.template +31 -0
  60. package/dist/templates/data-filler-full/package.json.template +18 -0
  61. package/dist/templates/data-filler-full/src/main.ts.template +412 -0
  62. package/dist/templates/data-filler-full/tsconfig.json +14 -0
  63. package/dist/templates/data-filler-full/ui.html.template +221 -0
  64. package/dist/templates/data-filler-lite/README.md.template +47 -0
  65. package/dist/templates/data-filler-lite/eslint.config.mjs +19 -0
  66. package/dist/templates/data-filler-lite/manifest.json.template +29 -0
  67. package/dist/templates/data-filler-lite/package.json.template +18 -0
  68. package/dist/templates/data-filler-lite/src/main.ts.template +222 -0
  69. package/dist/templates/data-filler-lite/tsconfig.json +14 -0
  70. package/dist/templates/data-filler-lite/ui.html.template +180 -0
  71. package/dist/templates/design-lint-panel/README.md.template +33 -0
  72. package/dist/templates/design-lint-panel/eslint.config.mjs +19 -0
  73. package/dist/templates/design-lint-panel/manifest.json.template +29 -0
  74. package/dist/templates/design-lint-panel/package.json.template +18 -0
  75. package/dist/templates/design-lint-panel/src/main.ts.template +221 -0
  76. package/dist/templates/design-lint-panel/tsconfig.json +14 -0
  77. package/dist/templates/design-lint-panel/ui.html.template +172 -0
  78. package/dist/templates/export-selection/README.md.template +26 -0
  79. package/dist/templates/export-selection/eslint.config.mjs +19 -0
  80. package/dist/templates/export-selection/manifest.json.template +31 -0
  81. package/dist/templates/export-selection/package.json.template +18 -0
  82. package/dist/templates/export-selection/src/main.ts.template +386 -0
  83. package/dist/templates/export-selection/tsconfig.json +14 -0
  84. package/dist/templates/export-selection/ui.html.template +163 -0
  85. package/dist/templates/review-submitter/README.md.template +24 -0
  86. package/dist/templates/review-submitter/eslint.config.mjs +19 -0
  87. package/dist/templates/review-submitter/manifest.json.template +35 -0
  88. package/dist/templates/review-submitter/package.json.template +18 -0
  89. package/dist/templates/review-submitter/src/main.ts.template +306 -0
  90. package/dist/templates/review-submitter/tsconfig.json +14 -0
  91. package/dist/templates/review-submitter/ui.html.template +114 -0
  92. package/dist/validate.d.ts +8 -0
  93. package/dist/validate.d.ts.map +1 -0
  94. package/dist/validate.js +42 -0
  95. package/dist/validate.js.map +1 -0
  96. package/package.json +46 -0
@@ -0,0 +1,177 @@
1
+ import { definePlugin } from "@lovision/plugin-sdk";
2
+
3
+ type Vec2 = {
4
+ x: number;
5
+ y: number;
6
+ };
7
+
8
+ type Size = {
9
+ height: number;
10
+ width: number;
11
+ };
12
+
13
+ type SampleAsset = {
14
+ id: "badge" | "hero";
15
+ fileName: string;
16
+ insertName: string;
17
+ label: string;
18
+ position: Vec2;
19
+ size: Size;
20
+ svg: string;
21
+ };
22
+
23
+ type AssetBrowserMessage =
24
+ | {
25
+ type: "error";
26
+ payload: {
27
+ message: string;
28
+ };
29
+ }
30
+ | {
31
+ type: "inserted";
32
+ payload: {
33
+ message: string;
34
+ newVersion: number;
35
+ resourceId: string;
36
+ };
37
+ }
38
+ | {
39
+ type: "ready";
40
+ payload: {
41
+ assets: Array<{
42
+ id: SampleAsset["id"];
43
+ label: string;
44
+ }>;
45
+ message: string;
46
+ };
47
+ };
48
+
49
+ const SAMPLE_ASSETS: readonly SampleAsset[] = [
50
+ {
51
+ id: "hero",
52
+ label: "Hero asset",
53
+ insertName: "Asset Browser Hero",
54
+ fileName: "asset-browser-hero.svg",
55
+ position: { x: 440, y: 120 },
56
+ size: { width: 240, height: 160 },
57
+ svg: `<svg xmlns="http://www.w3.org/2000/svg" width="240" height="160" viewBox="0 0 240 160"><rect width="240" height="160" rx="20" fill="#0f766e"/><circle cx="190" cy="44" r="28" fill="#facc15"/><path d="M28 118 82 70l42 36 28-24 60 36v20H28Z" fill="#ecfeff"/><text x="28" y="38" fill="#ecfeff" font-family="Arial, sans-serif" font-size="18" font-weight="700">Hero Asset</text></svg>`,
58
+ },
59
+ {
60
+ id: "badge",
61
+ label: "Badge asset",
62
+ insertName: "Asset Browser Badge",
63
+ fileName: "asset-browser-badge.svg",
64
+ position: { x: 460, y: 320 },
65
+ size: { width: 160, height: 160 },
66
+ svg: `<svg xmlns="http://www.w3.org/2000/svg" width="160" height="160" viewBox="0 0 160 160"><rect width="160" height="160" rx="80" fill="#4338ca"/><path d="m80 26 13 34 36 2-28 23 9 35-30-20-30 20 9-35-28-23 36-2Z" fill="#f8fafc"/><text x="80" y="132" text-anchor="middle" fill="#f8fafc" font-family="Arial, sans-serif" font-size="14" font-weight="700">Badge</text></svg>`,
67
+ },
68
+ ];
69
+
70
+ definePlugin({
71
+ apiVersion: "1.0",
72
+ version: "0.1.0",
73
+ command: async (ctx) => {
74
+ const session = await ctx.ui.show({
75
+ mode: "panel",
76
+ entry: "./ui.html",
77
+ title: "Asset Browser",
78
+ width: 420,
79
+ });
80
+
81
+ let closed = false;
82
+ const postToUi = async (message: AssetBrowserMessage): Promise<void> => {
83
+ if (closed) return;
84
+ await session.postMessage(message);
85
+ };
86
+ const panelClosed = new Promise<void>((resolve) => {
87
+ session.on("close", () => {
88
+ closed = true;
89
+ void ctx.notify.send("Asset Browser closed.", { kind: "info" });
90
+ resolve();
91
+ });
92
+ });
93
+
94
+ session.on("insert-asset", async (payload) => {
95
+ await handleUiAction(postToUi, async () => {
96
+ const asset = readSampleAsset(payload);
97
+ const snapshot = await ctx.document.snapshot();
98
+ const ref = await ctx.binary.create({
99
+ bytes: encodeUtf8(asset.svg),
100
+ mimeType: "image/svg+xml",
101
+ name: asset.fileName,
102
+ kind: "generated",
103
+ });
104
+ const result = await ctx.assets.insertImage({
105
+ source: ref,
106
+ position: { ...asset.position },
107
+ size: { ...asset.size },
108
+ name: asset.insertName,
109
+ expectedVersion: snapshot.version,
110
+ });
111
+ await postToUi({
112
+ type: "inserted",
113
+ payload: {
114
+ message: `Inserted ${asset.insertName}.`,
115
+ newVersion: result.newVersion,
116
+ resourceId: ref.id,
117
+ },
118
+ });
119
+ await ctx.notify.send(
120
+ `Asset Browser inserted ${asset.insertName}.`,
121
+ { kind: "success" },
122
+ );
123
+ });
124
+ });
125
+
126
+ await postToUi({
127
+ type: "ready",
128
+ payload: {
129
+ message: "Ready. Choose an asset and insert it as an image node.",
130
+ assets: SAMPLE_ASSETS.map((asset) => ({
131
+ id: asset.id,
132
+ label: asset.label,
133
+ })),
134
+ },
135
+ });
136
+
137
+ await panelClosed;
138
+ return { closed: true, ok: true };
139
+ },
140
+ });
141
+
142
+ async function handleUiAction(
143
+ postToUi: (message: AssetBrowserMessage) => Promise<void>,
144
+ action: () => Promise<unknown>,
145
+ ): Promise<void> {
146
+ try {
147
+ await action();
148
+ } catch (error) {
149
+ const message = error instanceof Error ? error.message : String(error);
150
+ await postToUi({ type: "error", payload: { message } });
151
+ }
152
+ }
153
+
154
+ function readSampleAsset(payload: unknown): SampleAsset {
155
+ if (!isRecord(payload) || typeof payload.assetId !== "string") {
156
+ throw new Error("Asset Browser requires an assetId.");
157
+ }
158
+ const asset = SAMPLE_ASSETS.find(
159
+ (candidate) => candidate.id === payload.assetId,
160
+ );
161
+ if (!asset) {
162
+ throw new Error(`Unknown asset: ${payload.assetId}`);
163
+ }
164
+ return asset;
165
+ }
166
+
167
+ function encodeUtf8(text: string): ArrayBuffer {
168
+ const bytes = new TextEncoder().encode(text);
169
+ return bytes.buffer.slice(
170
+ bytes.byteOffset,
171
+ bytes.byteOffset + bytes.byteLength,
172
+ );
173
+ }
174
+
175
+ function isRecord(value: unknown): value is Record<string, unknown> {
176
+ return value !== null && typeof value === "object" && !Array.isArray(value);
177
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["ESNext", "WebWorker"],
4
+ "module": "Preserve",
5
+ "moduleResolution": "bundler",
6
+ "target": "ESNext",
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "allowImportingTsExtensions": true,
10
+ "verbatimModuleSyntax": true,
11
+ "noEmit": true
12
+ },
13
+ "include": ["src/**/*"]
14
+ }
@@ -0,0 +1,137 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Asset Browser</title>
7
+ <style>
8
+ :root {
9
+ color-scheme: light dark;
10
+ }
11
+
12
+ body {
13
+ margin: 0;
14
+ color: CanvasText;
15
+ background: Canvas;
16
+ }
17
+
18
+ main {
19
+ display: grid;
20
+ gap: 1rem;
21
+ padding: 1rem;
22
+ }
23
+
24
+ .assets {
25
+ display: grid;
26
+ gap: 0.75rem;
27
+ }
28
+
29
+ .asset {
30
+ display: grid;
31
+ gap: 0.5rem;
32
+ padding: 0.75rem;
33
+ border: 1px solid ButtonBorder;
34
+ }
35
+
36
+ .asset-preview {
37
+ width: 100%;
38
+ min-height: 4rem;
39
+ display: grid;
40
+ place-items: center;
41
+ color: CanvasText;
42
+ background: Canvas;
43
+ border: 1px solid ButtonBorder;
44
+ }
45
+
46
+ .status {
47
+ min-height: 1.5rem;
48
+ }
49
+
50
+ pre {
51
+ max-height: 8rem;
52
+ overflow: auto;
53
+ padding: 0.75rem;
54
+ color: CanvasText;
55
+ background: Canvas;
56
+ border: 1px solid ButtonBorder;
57
+ }
58
+ </style>
59
+ </head>
60
+ <body>
61
+ <main>
62
+ <header>
63
+ <h1>Asset Browser</h1>
64
+ <p>Insert generated image assets through the plugin main worker.</p>
65
+ </header>
66
+
67
+ <section class="assets" aria-label="Assets">
68
+ <article class="asset">
69
+ <div class="asset-preview">Hero Asset</div>
70
+ <button id="insert-hero-button" type="button">Insert hero asset</button>
71
+ </article>
72
+ <article class="asset">
73
+ <div class="asset-preview">Badge Asset</div>
74
+ <button id="insert-badge-button" type="button">Insert badge asset</button>
75
+ </article>
76
+ </section>
77
+
78
+ <p id="status" class="status" role="status">Waiting for host...</p>
79
+ <pre id="details" aria-label="Details"></pre>
80
+ </main>
81
+
82
+ <script>
83
+ const uiId = globalThis.__LOVISION_PLUGIN_UI_ID__;
84
+ const status = document.getElementById("status");
85
+ const details = document.getElementById("details");
86
+
87
+ function post(type, payload) {
88
+ parent.postMessage(
89
+ {
90
+ type: "plugin-ui-message",
91
+ direction: "ui-to-main",
92
+ uiId,
93
+ message: { type, payload },
94
+ },
95
+ "*",
96
+ );
97
+ }
98
+
99
+ function insertAsset(assetId) {
100
+ status.textContent = "Inserting...";
101
+ details.textContent = "";
102
+ post("insert-asset", { assetId });
103
+ }
104
+
105
+ document.getElementById("insert-hero-button").addEventListener("click", () => {
106
+ insertAsset("hero");
107
+ });
108
+
109
+ document.getElementById("insert-badge-button").addEventListener("click", () => {
110
+ insertAsset("badge");
111
+ });
112
+
113
+ window.addEventListener("message", (event) => {
114
+ const envelope = event.data;
115
+ if (
116
+ !envelope ||
117
+ envelope.type !== "plugin-ui-message" ||
118
+ envelope.direction !== "main-to-ui" ||
119
+ envelope.uiId !== uiId
120
+ ) {
121
+ return;
122
+ }
123
+
124
+ const message = envelope.message;
125
+ if (message.type === "ready") {
126
+ status.textContent = message.payload.message;
127
+ details.textContent = JSON.stringify(message.payload.assets, null, 2);
128
+ } else if (message.type === "inserted") {
129
+ status.textContent = message.payload.message;
130
+ details.textContent = JSON.stringify(message.payload, null, 2);
131
+ } else if (message.type === "error") {
132
+ status.textContent = message.payload.message;
133
+ }
134
+ });
135
+ </script>
136
+ </body>
137
+ </html>
@@ -0,0 +1,34 @@
1
+ # __PLUGIN_NAME_EN__
2
+
3
+ An Instinct plugin scaffold generated by `create-instinct-plugin`.
4
+
5
+ ## Scripts
6
+
7
+ - `dev` — start the local HTTPS sideload server and print `manifestUrl`
8
+ - `build` — emit a formal-install bundle under `dist/`
9
+ - `validate` — run the same manifest + bundle validation path used by the host
10
+ - `lint` — run the default plugin ESLint setup
11
+
12
+ ## First Run
13
+
14
+ 1. Install dependencies with your preferred package manager.
15
+ 2. Run `dev`.
16
+ 3. Open the editor's plugin development surface and sideload the printed `manifestUrl`.
17
+ 4. Accept the local HTTPS certificate once if the browser asks.
18
+
19
+ ## Network Allowlist Example
20
+
21
+ The default manifest keeps permissions minimal. When you need fetch access, declare both:
22
+
23
+ - `network:fetch`
24
+ - `domain:api.example.com`
25
+
26
+ and pair them with:
27
+
28
+ ```json
29
+ "networkAccess": {
30
+ "allowedDomains": ["api.example.com"]
31
+ }
32
+ ```
33
+
34
+ That keeps Step 13 network enforcement on the documented happy path and avoids a first-run `network denied` surprise.
@@ -0,0 +1,19 @@
1
+ import pluginSdkConfig from "@lovision/plugin-sdk/eslint-config";
2
+ import tsParser from "@typescript-eslint/parser";
3
+
4
+ const pluginFiles = ["src/**/*.ts"];
5
+
6
+ export default [
7
+ {
8
+ files: pluginFiles,
9
+ languageOptions: {
10
+ parser: tsParser,
11
+ ecmaVersion: "latest",
12
+ sourceType: "module",
13
+ },
14
+ },
15
+ ...pluginSdkConfig.map((entry) => ({
16
+ ...entry,
17
+ files: pluginFiles,
18
+ })),
19
+ ];
@@ -0,0 +1,22 @@
1
+ {
2
+ "id": "__PLUGIN_ID__",
3
+ "name": {
4
+ "en": "__PLUGIN_NAME_EN__",
5
+ "zh-CN": "__PLUGIN_NAME_ZH__"
6
+ },
7
+ "version": "0.1.0",
8
+ "apiVersion": "1.0",
9
+ "editorType": ["design"],
10
+ "main": "./dist/main.js",
11
+ "documentAccess": "current-page",
12
+ "permissions": ["document:read", "notify"],
13
+ "commands": [
14
+ {
15
+ "id": "run",
16
+ "name": {
17
+ "en": "__COMMAND_NAME_EN__",
18
+ "zh-CN": "__COMMAND_NAME_ZH__"
19
+ }
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "__PACKAGE_NAME__",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "plugin-dev dev",
7
+ "build": "plugin-dev build",
8
+ "validate": "plugin-dev validate",
9
+ "lint": "eslint src --max-warnings=0"
10
+ },
11
+ "devDependencies": {
12
+ "@lovision/plugin-dev": "__PLUGIN_DEV_SPEC__",
13
+ "@lovision/plugin-sdk": "__PLUGIN_SDK_SPEC__"__LOCAL_SUPPORT_DEPS__,
14
+ "@typescript-eslint/parser": "^8.30.0",
15
+ "eslint": "^9.25.1",
16
+ "typescript": "^5.8.3"
17
+ }__LOCAL_OVERRIDES__
18
+ }
@@ -0,0 +1,20 @@
1
+ import { definePlugin } from "@lovision/plugin-sdk";
2
+
3
+ definePlugin({
4
+ apiVersion: "1.0",
5
+ version: "0.1.0",
6
+ command: async (ctx, info) => {
7
+ const snapshot = await ctx.document.snapshot();
8
+ const childCount = snapshot.root.children.length;
9
+ await ctx.notify.send(
10
+ `Ran ${info.id} with ${childCount} top-level node(s)`,
11
+ { kind: "success" },
12
+ );
13
+
14
+ return {
15
+ childCount,
16
+ commandId: info.id,
17
+ ok: true,
18
+ };
19
+ },
20
+ });
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["ESNext", "WebWorker"],
4
+ "module": "Preserve",
5
+ "moduleResolution": "bundler",
6
+ "target": "ESNext",
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "allowImportingTsExtensions": true,
10
+ "verbatimModuleSyntax": true,
11
+ "noEmit": true
12
+ },
13
+ "include": ["src/**/*"]
14
+ }
@@ -0,0 +1,24 @@
1
+ # Batch Layout Organizer
2
+
3
+ This Step 27 acceptance template proves the structural Host Facade through a real `plugin-dev` project. The panel asks the main worker to create demo cards when the scene is sparse, then runs `group / reparent / reorder` through `ctx.nodes`.
4
+
5
+ ## Development
6
+
7
+ ```bash
8
+ npm install
9
+ npm run dev
10
+ ```
11
+
12
+ Copy the printed `manifestUrl=...`, open the editor shell, then use Dev Panel -> Development -> Add by URL.
13
+
14
+ ## Use
15
+
16
+ Run **Open Batch Layout Organizer**, click **Analyze canvas**, then click **Organize layout**. The command creates a `Batch Layout Group`, reparents a promoted card into it, reorders that card to the top, focuses the group, and emits a notification.
17
+
18
+ ## Formal install
19
+
20
+ ```bash
21
+ npm run build
22
+ ```
23
+
24
+ Upload the generated `dist/*.bundle.json` through Dev Panel -> Formal install, confirm the install dialog, then run the command from MainMenu.
@@ -0,0 +1,19 @@
1
+ import pluginSdkConfig from "@lovision/plugin-sdk/eslint-config";
2
+ import tsParser from "@typescript-eslint/parser";
3
+
4
+ const pluginFiles = ["src/**/*.ts"];
5
+
6
+ export default [
7
+ {
8
+ files: pluginFiles,
9
+ languageOptions: {
10
+ parser: tsParser,
11
+ ecmaVersion: "latest",
12
+ sourceType: "module",
13
+ },
14
+ },
15
+ ...pluginSdkConfig.map((entry) => ({
16
+ ...entry,
17
+ files: pluginFiles,
18
+ })),
19
+ ];
@@ -0,0 +1,31 @@
1
+ {
2
+ "id": "__PLUGIN_ID__",
3
+ "name": {
4
+ "en": "Batch Layout Organizer",
5
+ "zh-CN": "Batch Layout Organizer"
6
+ },
7
+ "version": "0.1.0",
8
+ "apiVersion": "1.0",
9
+ "editorType": ["design"],
10
+ "main": "./dist/main.js",
11
+ "ui": "./ui.html",
12
+ "documentAccess": "current-page",
13
+ "permissions": [
14
+ "document:read",
15
+ "document:write.create",
16
+ "document:write.reparent",
17
+ "notify",
18
+ "selection:read",
19
+ "ui:panel",
20
+ "viewport:write"
21
+ ],
22
+ "commands": [
23
+ {
24
+ "id": "run",
25
+ "name": {
26
+ "en": "__COMMAND_NAME_EN__",
27
+ "zh-CN": "__COMMAND_NAME_ZH__"
28
+ }
29
+ }
30
+ ]
31
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "__PACKAGE_NAME__",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "plugin-dev dev",
7
+ "build": "plugin-dev build",
8
+ "validate": "plugin-dev validate",
9
+ "lint": "eslint src --max-warnings=0"
10
+ },
11
+ "devDependencies": {
12
+ "@lovision/plugin-dev": "__PLUGIN_DEV_SPEC__",
13
+ "@lovision/plugin-sdk": "__PLUGIN_SDK_SPEC__"__LOCAL_SUPPORT_DEPS__,
14
+ "@typescript-eslint/parser": "^8.30.0",
15
+ "eslint": "^9.25.1",
16
+ "typescript": "^5.8.3"
17
+ }__LOCAL_OVERRIDES__
18
+ }