@gtkx/cli 0.20.0 → 1.0.0-rc.1
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/README.md +173 -0
- package/bin/gtkx-dev-runner.js +9 -0
- package/dist/builder.d.ts +2 -50
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +22 -48
- package/dist/builder.js.map +1 -1
- package/dist/cli.d.ts +1 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -124
- package/dist/cli.js.map +1 -1
- package/dist/codegen/freshness.d.ts +11 -0
- package/dist/codegen/freshness.d.ts.map +1 -0
- package/dist/codegen/freshness.js +42 -0
- package/dist/codegen/freshness.js.map +1 -0
- package/dist/codegen/report.d.ts +3 -0
- package/dist/codegen/report.d.ts.map +1 -0
- package/dist/codegen/report.js +16 -0
- package/dist/codegen/report.js.map +1 -0
- package/dist/codegen/run-codegen.d.ts +35 -0
- package/dist/codegen/run-codegen.d.ts.map +1 -0
- package/dist/codegen/run-codegen.js +133 -0
- package/dist/codegen/run-codegen.js.map +1 -0
- package/dist/codegen/store-resolver.d.ts +21 -0
- package/dist/codegen/store-resolver.d.ts.map +1 -0
- package/dist/codegen/store-resolver.js +52 -0
- package/dist/codegen/store-resolver.js.map +1 -0
- package/dist/commands/build.d.ts +9 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +33 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/codegen.d.ts +9 -0
- package/dist/commands/codegen.d.ts.map +1 -0
- package/dist/commands/codegen.js +40 -0
- package/dist/commands/codegen.js.map +1 -0
- package/dist/commands/dev.d.ts +5 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +21 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/docs.d.ts +19 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +59 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/dev/icon-dir.d.ts +2 -0
- package/dist/dev/icon-dir.d.ts.map +1 -0
- package/dist/dev/icon-dir.js +13 -0
- package/dist/dev/icon-dir.js.map +1 -0
- package/dist/dev/refresh-tracker.d.ts +6 -0
- package/dist/dev/refresh-tracker.d.ts.map +1 -0
- package/dist/dev/refresh-tracker.js +18 -0
- package/dist/dev/refresh-tracker.js.map +1 -0
- package/dist/dev/runner-deps.d.ts +3 -0
- package/dist/dev/runner-deps.d.ts.map +1 -0
- package/dist/dev/runner-deps.js +39 -0
- package/dist/dev/runner-deps.js.map +1 -0
- package/dist/dev/runner-main.d.ts +2 -0
- package/dist/dev/runner-main.d.ts.map +1 -0
- package/dist/dev/runner-main.js +28 -0
- package/dist/dev/runner-main.js.map +1 -0
- package/dist/dev/runner.d.ts +23 -0
- package/dist/dev/runner.d.ts.map +1 -0
- package/dist/dev/runner.js +88 -0
- package/dist/dev/runner.js.map +1 -0
- package/dist/dev/schema-dir.d.ts +2 -0
- package/dist/dev/schema-dir.d.ts.map +1 -0
- package/dist/dev/schema-dir.js +10 -0
- package/dist/dev/schema-dir.js.map +1 -0
- package/dist/dev/supervisor.d.ts +17 -0
- package/dist/dev/supervisor.d.ts.map +1 -0
- package/dist/dev/supervisor.js +142 -0
- package/dist/dev/supervisor.js.map +1 -0
- package/dist/dev/vite-dev-server.d.ts +21 -0
- package/dist/dev/vite-dev-server.d.ts.map +1 -0
- package/dist/dev/vite-dev-server.js +12 -0
- package/dist/dev/vite-dev-server.js.map +1 -0
- package/dist/internal/banner.d.ts +3 -0
- package/dist/internal/banner.d.ts.map +1 -0
- package/dist/internal/banner.js +8 -0
- package/dist/internal/banner.js.map +1 -0
- package/dist/internal/data-dir.d.ts +3 -0
- package/dist/internal/data-dir.d.ts.map +1 -0
- package/dist/internal/data-dir.js +37 -0
- package/dist/internal/data-dir.js.map +1 -0
- package/dist/internal/entry-arg.d.ts +19 -0
- package/dist/internal/entry-arg.d.ts.map +1 -0
- package/dist/internal/entry-arg.js +33 -0
- package/dist/internal/entry-arg.js.map +1 -0
- package/dist/internal/errors.d.ts +4 -0
- package/dist/internal/errors.d.ts.map +1 -0
- package/dist/internal/errors.js +27 -0
- package/dist/internal/errors.js.map +1 -0
- package/dist/internal/list-files.d.ts +6 -0
- package/dist/internal/list-files.d.ts.map +1 -0
- package/dist/internal/list-files.js +13 -0
- package/dist/internal/list-files.js.map +1 -0
- package/dist/internal/resolve-cli-tool.d.ts +2 -0
- package/dist/internal/resolve-cli-tool.d.ts.map +1 -0
- package/dist/internal/resolve-cli-tool.js +20 -0
- package/dist/internal/resolve-cli-tool.js.map +1 -0
- package/dist/internal/staging-dir.d.ts +3 -0
- package/dist/internal/staging-dir.d.ts.map +1 -0
- package/dist/internal/staging-dir.js +16 -0
- package/dist/internal/staging-dir.js.map +1 -0
- package/dist/internal/xdg-data-dirs.d.ts +2 -0
- package/dist/internal/xdg-data-dirs.d.ts.map +1 -0
- package/dist/internal/xdg-data-dirs.js +8 -0
- package/dist/internal/xdg-data-dirs.js.map +1 -0
- package/dist/mcp/client.d.ts +23 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +157 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/handlers.d.ts +8 -0
- package/dist/mcp/handlers.d.ts.map +1 -0
- package/dist/mcp/handlers.js +119 -0
- package/dist/mcp/handlers.js.map +1 -0
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +17 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/serialize-widget.d.ts +10 -0
- package/dist/mcp/serialize-widget.d.ts.map +1 -0
- package/dist/mcp/serialize-widget.js +20 -0
- package/dist/mcp/serialize-widget.js.map +1 -0
- package/dist/mcp/testing-loader.d.ts +6 -0
- package/dist/mcp/testing-loader.d.ts.map +1 -0
- package/dist/mcp/testing-loader.js +25 -0
- package/dist/mcp/testing-loader.js.map +1 -0
- package/dist/mcp/widget-registry.d.ts +13 -0
- package/dist/mcp/widget-registry.d.ts.map +1 -0
- package/dist/mcp/widget-registry.js +38 -0
- package/dist/mcp/widget-registry.js.map +1 -0
- package/dist/refresh-runtime.d.ts +10 -20
- package/dist/refresh-runtime.d.ts.map +1 -1
- package/dist/refresh-runtime.js +12 -36
- package/dist/refresh-runtime.js.map +1 -1
- package/dist/settings/compile.d.ts +2 -0
- package/dist/settings/compile.d.ts.map +1 -0
- package/dist/settings/compile.js +19 -0
- package/dist/settings/compile.js.map +1 -0
- package/dist/settings/parser.d.ts +23 -0
- package/dist/settings/parser.d.ts.map +1 -0
- package/dist/settings/parser.js +117 -0
- package/dist/settings/parser.js.map +1 -0
- package/dist/settings/render.d.ts +4 -0
- package/dist/settings/render.d.ts.map +1 -0
- package/dist/settings/render.js +148 -0
- package/dist/settings/render.js.map +1 -0
- package/dist/settings/schema.d.ts +13 -0
- package/dist/settings/schema.d.ts.map +1 -0
- package/dist/settings/schema.js +103 -0
- package/dist/settings/schema.js.map +1 -0
- package/dist/vite-plugins/asset-extensions.d.ts +4 -0
- package/dist/vite-plugins/asset-extensions.d.ts.map +1 -0
- package/dist/vite-plugins/asset-extensions.js +27 -0
- package/dist/vite-plugins/asset-extensions.js.map +1 -0
- package/dist/vite-plugins/built-url.d.ts +3 -0
- package/dist/vite-plugins/built-url.d.ts.map +1 -0
- package/dist/{vite-plugin-gtkx-built-url.js → vite-plugins/built-url.js} +4 -17
- package/dist/vite-plugins/built-url.js.map +1 -0
- package/dist/vite-plugins/css.d.ts +3 -0
- package/dist/vite-plugins/css.d.ts.map +1 -0
- package/dist/vite-plugins/css.js +30 -0
- package/dist/vite-plugins/css.js.map +1 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts +7 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts.map +1 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.js +15 -0
- package/dist/vite-plugins/fast-refresh/refresh-filter.js.map +1 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts +5 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts.map +1 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.js +66 -0
- package/dist/vite-plugins/fast-refresh/swc-refresh.js.map +1 -0
- package/dist/vite-plugins/icons.d.ts +3 -0
- package/dist/vite-plugins/icons.d.ts.map +1 -0
- package/dist/vite-plugins/icons.js +47 -0
- package/dist/vite-plugins/icons.js.map +1 -0
- package/dist/vite-plugins/index.d.ts +3 -0
- package/dist/vite-plugins/index.d.ts.map +1 -0
- package/dist/vite-plugins/index.js +21 -0
- package/dist/vite-plugins/index.js.map +1 -0
- package/dist/vite-plugins/native.d.ts +3 -0
- package/dist/vite-plugins/native.d.ts.map +1 -0
- package/dist/vite-plugins/native.js +60 -0
- package/dist/vite-plugins/native.js.map +1 -0
- package/dist/vite-plugins/react-compiler.d.ts +4 -0
- package/dist/vite-plugins/react-compiler.d.ts.map +1 -0
- package/dist/vite-plugins/react-compiler.js +56 -0
- package/dist/vite-plugins/react-compiler.js.map +1 -0
- package/dist/vite-plugins/react-dom-prebundle.d.ts +3 -0
- package/dist/vite-plugins/react-dom-prebundle.d.ts.map +1 -0
- package/dist/vite-plugins/react-dom-prebundle.js +11 -0
- package/dist/vite-plugins/react-dom-prebundle.js.map +1 -0
- package/dist/vite-plugins/resource-init-module.d.ts +5 -0
- package/dist/vite-plugins/resource-init-module.d.ts.map +1 -0
- package/dist/vite-plugins/resource-init-module.js +50 -0
- package/dist/vite-plugins/resource-init-module.js.map +1 -0
- package/dist/vite-plugins/resource-shared.d.ts +6 -0
- package/dist/vite-plugins/resource-shared.d.ts.map +1 -0
- package/dist/vite-plugins/resource-shared.js +21 -0
- package/dist/vite-plugins/resource-shared.js.map +1 -0
- package/dist/vite-plugins/resources.d.ts +4 -0
- package/dist/vite-plugins/resources.d.ts.map +1 -0
- package/dist/vite-plugins/resources.js +220 -0
- package/dist/vite-plugins/resources.js.map +1 -0
- package/dist/vite-plugins/settings-worker-env.d.ts +3 -0
- package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -0
- package/dist/vite-plugins/settings-worker-env.js +21 -0
- package/dist/vite-plugins/settings-worker-env.js.map +1 -0
- package/dist/vite-plugins/settings.d.ts +3 -0
- package/dist/vite-plugins/settings.d.ts.map +1 -0
- package/dist/vite-plugins/settings.js +190 -0
- package/dist/vite-plugins/settings.js.map +1 -0
- package/dist/vite-plugins/undeclared-library.d.ts +3 -0
- package/dist/vite-plugins/undeclared-library.d.ts.map +1 -0
- package/dist/vite-plugins/undeclared-library.js +47 -0
- package/dist/vite-plugins/undeclared-library.js.map +1 -0
- package/dist/vite-plugins/virtual-module.d.ts +22 -0
- package/dist/vite-plugins/virtual-module.d.ts.map +1 -0
- package/dist/vite-plugins/virtual-module.js +18 -0
- package/dist/vite-plugins/virtual-module.js.map +1 -0
- package/dist/vitest-plugin.d.ts +10 -0
- package/dist/vitest-plugin.d.ts.map +1 -0
- package/dist/vitest-plugin.js +12 -0
- package/dist/vitest-plugin.js.map +1 -0
- package/env.d.ts +131 -3
- package/package.json +32 -38
- package/src/babel-modules.d.ts +13 -0
- package/src/builder.ts +28 -76
- package/src/cli.ts +22 -0
- package/src/codegen/freshness.ts +52 -0
- package/src/codegen/report.ts +20 -0
- package/src/codegen/run-codegen.ts +185 -0
- package/src/codegen/store-resolver.ts +75 -0
- package/src/commands/build.ts +37 -0
- package/src/commands/codegen.ts +44 -0
- package/src/commands/dev.ts +25 -0
- package/src/commands/docs.ts +66 -0
- package/src/dev/icon-dir.ts +11 -0
- package/src/dev/refresh-tracker.ts +21 -0
- package/src/dev/runner-deps.ts +42 -0
- package/src/dev/runner-main.ts +33 -0
- package/src/dev/runner.ts +126 -0
- package/src/dev/schema-dir.ts +11 -0
- package/src/dev/supervisor.ts +178 -0
- package/src/dev/vite-dev-server.ts +34 -0
- package/src/internal/banner.ts +9 -0
- package/src/internal/data-dir.ts +39 -0
- package/src/internal/entry-arg.ts +38 -0
- package/src/internal/errors.ts +28 -0
- package/src/internal/list-files.ts +17 -0
- package/src/internal/resolve-cli-tool.ts +21 -0
- package/src/internal/staging-dir.ts +16 -0
- package/src/internal/xdg-data-dirs.ts +7 -0
- package/src/mcp/client.ts +177 -0
- package/src/mcp/handlers.ts +156 -0
- package/src/mcp/index.ts +22 -0
- package/src/mcp/serialize-widget.ts +34 -0
- package/src/mcp/testing-loader.ts +32 -0
- package/src/mcp/widget-registry.ts +43 -0
- package/src/refresh-runtime.ts +12 -41
- package/src/settings/compile.ts +19 -0
- package/src/settings/parser.ts +154 -0
- package/src/settings/render.ts +167 -0
- package/src/settings/schema.ts +115 -0
- package/src/vite-plugins/asset-extensions.ts +28 -0
- package/src/{vite-plugin-gtkx-built-url.ts → vite-plugins/built-url.ts} +3 -16
- package/src/vite-plugins/css.ts +38 -0
- package/src/vite-plugins/fast-refresh/refresh-filter.ts +18 -0
- package/src/vite-plugins/fast-refresh/swc-refresh.ts +79 -0
- package/src/vite-plugins/icons.ts +58 -0
- package/src/vite-plugins/index.ts +22 -0
- package/src/vite-plugins/native.ts +74 -0
- package/src/vite-plugins/react-compiler.ts +70 -0
- package/src/vite-plugins/react-dom-prebundle.ts +14 -0
- package/src/vite-plugins/resource-init-module.ts +54 -0
- package/src/vite-plugins/resource-shared.ts +27 -0
- package/src/vite-plugins/resources.ts +272 -0
- package/src/vite-plugins/settings-worker-env.ts +25 -0
- package/src/vite-plugins/settings.ts +221 -0
- package/src/vite-plugins/undeclared-library.ts +69 -0
- package/src/vite-plugins/virtual-module.ts +37 -0
- package/src/vitest-plugin.ts +14 -0
- package/dist/create.d.ts +0 -54
- package/dist/create.d.ts.map +0 -1
- package/dist/create.js +0 -221
- package/dist/create.js.map +0 -1
- package/dist/dev-server.d.ts +0 -38
- package/dist/dev-server.d.ts.map +0 -1
- package/dist/dev-server.js +0 -128
- package/dist/dev-server.js.map +0 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -4
- package/dist/index.js.map +0 -1
- package/dist/mcp-client.d.ts +0 -31
- package/dist/mcp-client.d.ts.map +0 -1
- package/dist/mcp-client.js +0 -430
- package/dist/mcp-client.js.map +0 -1
- package/dist/templates.d.ts +0 -9
- package/dist/templates.d.ts.map +0 -1
- package/dist/templates.js +0 -16
- package/dist/templates.js.map +0 -1
- package/dist/vite-plugin-gtkx-assets.d.ts +0 -20
- package/dist/vite-plugin-gtkx-assets.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-assets.js +0 -57
- package/dist/vite-plugin-gtkx-assets.js.map +0 -1
- package/dist/vite-plugin-gtkx-built-url.d.ts +0 -18
- package/dist/vite-plugin-gtkx-built-url.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-built-url.js.map +0 -1
- package/dist/vite-plugin-gtkx-native.d.ts +0 -14
- package/dist/vite-plugin-gtkx-native.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-native.js +0 -53
- package/dist/vite-plugin-gtkx-native.js.map +0 -1
- package/dist/vite-plugin-gtkx-refresh.d.ts +0 -8
- package/dist/vite-plugin-gtkx-refresh.d.ts.map +0 -1
- package/dist/vite-plugin-gtkx-refresh.js +0 -37
- package/dist/vite-plugin-gtkx-refresh.js.map +0 -1
- package/dist/vite-plugin-swc-ssr-refresh.d.ts +0 -8
- package/dist/vite-plugin-swc-ssr-refresh.d.ts.map +0 -1
- package/dist/vite-plugin-swc-ssr-refresh.js +0 -46
- package/dist/vite-plugin-swc-ssr-refresh.js.map +0 -1
- package/src/cli.tsx +0 -154
- package/src/create.ts +0 -310
- package/src/dev-server.tsx +0 -162
- package/src/index.ts +0 -3
- package/src/mcp-client.ts +0 -518
- package/src/templates.ts +0 -26
- package/src/vite-plugin-gtkx-assets.ts +0 -69
- package/src/vite-plugin-gtkx-native.ts +0 -64
- package/src/vite-plugin-gtkx-refresh.ts +0 -54
- package/src/vite-plugin-swc-ssr-refresh.ts +0 -61
- package/templates/claude/EXAMPLES.md.ejs +0 -744
- package/templates/claude/SKILL.md.ejs +0 -93
- package/templates/claude/WIDGETS.md.ejs +0 -820
- package/templates/config/vitest.config.ts.ejs +0 -11
- package/templates/gitignore.ejs +0 -4
- package/templates/package.json.ejs +0 -16
- package/templates/src/app.tsx.ejs +0 -32
- package/templates/src/dev.tsx.ejs +0 -5
- package/templates/src/gtkx-env.d.ts.ejs +0 -1
- package/templates/src/index.tsx.ejs +0 -5
- package/templates/tests/app.test.tsx.ejs +0 -16
- package/templates/tsconfig.json.ejs +0 -14
package/src/mcp-client.ts
DELETED
|
@@ -1,518 +0,0 @@
|
|
|
1
|
-
import * as net from "node:net";
|
|
2
|
-
import { getNativeInterface } from "@gtkx/ffi";
|
|
3
|
-
import * as Gio from "@gtkx/ffi/gio";
|
|
4
|
-
import { Value } from "@gtkx/ffi/gobject";
|
|
5
|
-
import * as Gtk from "@gtkx/ffi/gtk";
|
|
6
|
-
import {
|
|
7
|
-
DEFAULT_SOCKET_PATH,
|
|
8
|
-
type IpcMethod,
|
|
9
|
-
type IpcRequest,
|
|
10
|
-
IpcRequestSchema,
|
|
11
|
-
type IpcResponse,
|
|
12
|
-
IpcResponseSchema,
|
|
13
|
-
McpError,
|
|
14
|
-
McpErrorCode,
|
|
15
|
-
methodNotFoundError,
|
|
16
|
-
type SerializedWidget,
|
|
17
|
-
widgetNotFoundError,
|
|
18
|
-
} from "@gtkx/mcp";
|
|
19
|
-
|
|
20
|
-
const widgetIdMap = new WeakMap<Gtk.Widget, string>();
|
|
21
|
-
let nextWidgetId = 0;
|
|
22
|
-
|
|
23
|
-
const getWidgetId = (widget: Gtk.Widget): string => {
|
|
24
|
-
let id = widgetIdMap.get(widget);
|
|
25
|
-
if (!id) {
|
|
26
|
-
id = String(nextWidgetId++);
|
|
27
|
-
widgetIdMap.set(widget, id);
|
|
28
|
-
}
|
|
29
|
-
return id;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
type TestingModule = typeof import("@gtkx/testing");
|
|
33
|
-
|
|
34
|
-
let testingModule: TestingModule | null = null;
|
|
35
|
-
let testingLoadError: Error | null = null;
|
|
36
|
-
|
|
37
|
-
const loadTestingModule = async (): Promise<TestingModule> => {
|
|
38
|
-
if (testingModule) return testingModule;
|
|
39
|
-
if (testingLoadError) throw testingLoadError;
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
testingModule = await import("@gtkx/testing");
|
|
43
|
-
return testingModule;
|
|
44
|
-
} catch (cause) {
|
|
45
|
-
testingLoadError = new Error(
|
|
46
|
-
"@gtkx/testing is not installed. Install it to enable MCP widget interactions: pnpm add -D @gtkx/testing",
|
|
47
|
-
{ cause },
|
|
48
|
-
);
|
|
49
|
-
throw testingLoadError;
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
type McpClientOptions = {
|
|
54
|
-
socketPath?: string;
|
|
55
|
-
appId: string;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
type PendingRequest = {
|
|
59
|
-
resolve: (result: unknown) => void;
|
|
60
|
-
reject: (error: Error) => void;
|
|
61
|
-
timeout: NodeJS.Timeout;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const RECONNECT_DELAY_MS = 2000;
|
|
65
|
-
const REQUEST_TIMEOUT_MS = 30000;
|
|
66
|
-
|
|
67
|
-
const formatRole = (role: Gtk.AccessibleRole | undefined): string => {
|
|
68
|
-
if (role === undefined) return "UNKNOWN";
|
|
69
|
-
return Gtk.AccessibleRole[role] ?? String(role);
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const getWidgetText = (widget: Gtk.Widget): string | null => {
|
|
73
|
-
if ("getLabel" in widget && typeof widget.getLabel === "function") {
|
|
74
|
-
return widget.getLabel() ?? null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if ("getText" in widget && typeof widget.getText === "function") {
|
|
78
|
-
return widget.getText() ?? null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if ("getTitle" in widget && typeof widget.getTitle === "function") {
|
|
82
|
-
return widget.getTitle() ?? null;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return getNativeInterface(widget, Gtk.Editable)?.getText() ?? null;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const serializeWidget = (widget: Gtk.Widget): SerializedWidget => {
|
|
89
|
-
const children: SerializedWidget[] = [];
|
|
90
|
-
let child = widget.getFirstChild();
|
|
91
|
-
while (child) {
|
|
92
|
-
children.push(serializeWidget(child));
|
|
93
|
-
child = child.getNextSibling();
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const text = getWidgetText(widget);
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
id: getWidgetId(widget),
|
|
100
|
-
type: widget.constructor.name,
|
|
101
|
-
role: formatRole(widget.getAccessibleRole()),
|
|
102
|
-
name: widget.getName() || null,
|
|
103
|
-
label: text,
|
|
104
|
-
text,
|
|
105
|
-
sensitive: widget.getSensitive(),
|
|
106
|
-
visible: widget.getVisible(),
|
|
107
|
-
cssClasses: widget.getCssClasses() ?? [],
|
|
108
|
-
children,
|
|
109
|
-
};
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const widgetRegistry = new Map<string, Gtk.Widget>();
|
|
113
|
-
|
|
114
|
-
const registerWidgets = (widget: Gtk.Widget): void => {
|
|
115
|
-
const idStr = getWidgetId(widget);
|
|
116
|
-
widgetRegistry.set(idStr, widget);
|
|
117
|
-
let child = widget.getFirstChild();
|
|
118
|
-
while (child) {
|
|
119
|
-
registerWidgets(child);
|
|
120
|
-
child = child.getNextSibling();
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const getWidgetById = (id: string): Gtk.Widget | undefined => {
|
|
125
|
-
return widgetRegistry.get(id);
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
const refreshWidgetRegistry = (): void => {
|
|
129
|
-
widgetRegistry.clear();
|
|
130
|
-
const windows = Gtk.Window.listToplevels();
|
|
131
|
-
for (const window of windows) {
|
|
132
|
-
registerWidgets(window);
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
class McpClient {
|
|
137
|
-
private socket: net.Socket | null = null;
|
|
138
|
-
private buffer = "";
|
|
139
|
-
private socketPath: string;
|
|
140
|
-
private appId: string;
|
|
141
|
-
private reconnectTimer: NodeJS.Timeout | null = null;
|
|
142
|
-
private hasConnected = false;
|
|
143
|
-
private isStopping = false;
|
|
144
|
-
private pendingRequests = new Map<string, PendingRequest>();
|
|
145
|
-
|
|
146
|
-
constructor(options: McpClientOptions) {
|
|
147
|
-
this.socketPath = options.socketPath ?? DEFAULT_SOCKET_PATH;
|
|
148
|
-
this.appId = options.appId;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async connect(): Promise<void> {
|
|
152
|
-
return new Promise((resolve, reject) => {
|
|
153
|
-
this.attemptConnect(resolve, reject);
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
private attemptConnect(onSuccess?: () => void, onError?: (error: Error) => void): void {
|
|
158
|
-
let settled = false;
|
|
159
|
-
|
|
160
|
-
const settle = <T extends unknown[]>(callback: ((...args: T) => void) | undefined, ...args: T) => {
|
|
161
|
-
if (settled) return;
|
|
162
|
-
settled = true;
|
|
163
|
-
callback?.(...args);
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
this.socket = net.createConnection(this.socketPath, () => {
|
|
167
|
-
console.log(`[gtkx] Connected to MCP server at ${this.socketPath}`);
|
|
168
|
-
this.hasConnected = true;
|
|
169
|
-
this.register()
|
|
170
|
-
.then(() => {
|
|
171
|
-
console.log("[gtkx] Registered with MCP server");
|
|
172
|
-
settle(onSuccess);
|
|
173
|
-
})
|
|
174
|
-
.catch((error) => {
|
|
175
|
-
console.error("[gtkx] Failed to register with MCP server:", error.message);
|
|
176
|
-
settle(onError, error instanceof Error ? error : new Error(String(error)));
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
this.socket.on("data", (data: Buffer) => this.handleData(data));
|
|
181
|
-
|
|
182
|
-
this.socket.on("close", () => {
|
|
183
|
-
if (this.hasConnected) {
|
|
184
|
-
console.log("[gtkx] Disconnected from MCP server");
|
|
185
|
-
this.hasConnected = false;
|
|
186
|
-
}
|
|
187
|
-
this.socket = null;
|
|
188
|
-
this.rejectPendingRequests(new Error("Connection closed"));
|
|
189
|
-
this.scheduleReconnect();
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
this.socket.on("error", (error) => {
|
|
193
|
-
const code = (error as NodeJS.ErrnoException).code;
|
|
194
|
-
const isDisconnectError =
|
|
195
|
-
code === "ENOENT" || code === "ECONNREFUSED" || code === "EPIPE" || code === "ECONNRESET";
|
|
196
|
-
if (isDisconnectError) {
|
|
197
|
-
this.scheduleReconnect();
|
|
198
|
-
} else {
|
|
199
|
-
console.error("[gtkx] Socket error:", error.message);
|
|
200
|
-
}
|
|
201
|
-
settle(onError, error);
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
private scheduleReconnect(): void {
|
|
206
|
-
if (this.reconnectTimer || this.isStopping) return;
|
|
207
|
-
this.reconnectTimer = setTimeout(() => {
|
|
208
|
-
this.reconnectTimer = null;
|
|
209
|
-
this.attemptConnect();
|
|
210
|
-
}, RECONNECT_DELAY_MS);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
disconnect(): void {
|
|
214
|
-
this.isStopping = true;
|
|
215
|
-
if (this.reconnectTimer) {
|
|
216
|
-
clearTimeout(this.reconnectTimer);
|
|
217
|
-
this.reconnectTimer = null;
|
|
218
|
-
}
|
|
219
|
-
this.rejectPendingRequests(new Error("Client disconnected"));
|
|
220
|
-
if (this.socket) {
|
|
221
|
-
this.send({ id: crypto.randomUUID(), method: "app.unregister" });
|
|
222
|
-
this.socket.destroy();
|
|
223
|
-
this.socket = null;
|
|
224
|
-
}
|
|
225
|
-
this.hasConnected = false;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
private rejectPendingRequests(error: Error): void {
|
|
229
|
-
for (const pending of this.pendingRequests.values()) {
|
|
230
|
-
clearTimeout(pending.timeout);
|
|
231
|
-
pending.reject(error);
|
|
232
|
-
}
|
|
233
|
-
this.pendingRequests.clear();
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
private async register(): Promise<void> {
|
|
237
|
-
await this.sendRequest("app.register", {
|
|
238
|
-
appId: this.appId,
|
|
239
|
-
pid: process.pid,
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
private send(message: IpcRequest | IpcResponse): void {
|
|
244
|
-
if (!this.socket || !this.socket.writable) return;
|
|
245
|
-
this.socket.write(`${JSON.stringify(message)}\n`);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
private sendRequest(method: IpcMethod, params?: unknown): Promise<unknown> {
|
|
249
|
-
return new Promise((resolve, reject) => {
|
|
250
|
-
if (!this.socket || !this.socket.writable) {
|
|
251
|
-
reject(new Error("Socket not connected"));
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const id = crypto.randomUUID();
|
|
256
|
-
const timeout = setTimeout(() => {
|
|
257
|
-
this.pendingRequests.delete(id);
|
|
258
|
-
reject(new Error(`Request timed out: ${method}`));
|
|
259
|
-
}, REQUEST_TIMEOUT_MS);
|
|
260
|
-
|
|
261
|
-
this.pendingRequests.set(id, { resolve, reject, timeout });
|
|
262
|
-
this.send({ id, method, params });
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
private handleData(data: Buffer): void {
|
|
267
|
-
this.buffer += data.toString();
|
|
268
|
-
|
|
269
|
-
let newlineIndex = this.buffer.indexOf("\n");
|
|
270
|
-
while (newlineIndex !== -1) {
|
|
271
|
-
const line = this.buffer.slice(0, newlineIndex);
|
|
272
|
-
this.buffer = this.buffer.slice(newlineIndex + 1);
|
|
273
|
-
|
|
274
|
-
if (line.trim()) {
|
|
275
|
-
this.processMessage(line);
|
|
276
|
-
}
|
|
277
|
-
newlineIndex = this.buffer.indexOf("\n");
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
private processMessage(line: string): void {
|
|
282
|
-
let parsed: unknown;
|
|
283
|
-
try {
|
|
284
|
-
parsed = JSON.parse(line);
|
|
285
|
-
} catch {
|
|
286
|
-
console.warn("[gtkx] Received invalid JSON from MCP server");
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const responseResult = IpcResponseSchema.safeParse(parsed);
|
|
291
|
-
if (responseResult.success) {
|
|
292
|
-
const response = responseResult.data;
|
|
293
|
-
const pending = this.pendingRequests.get(response.id);
|
|
294
|
-
if (pending) {
|
|
295
|
-
clearTimeout(pending.timeout);
|
|
296
|
-
this.pendingRequests.delete(response.id);
|
|
297
|
-
if (response.error) {
|
|
298
|
-
pending.reject(new Error(response.error.message));
|
|
299
|
-
} else {
|
|
300
|
-
pending.resolve(response.result);
|
|
301
|
-
}
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const requestResult = IpcRequestSchema.safeParse(parsed);
|
|
307
|
-
if (!requestResult.success) {
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
this.handleRequest(requestResult.data).catch((error) => {
|
|
312
|
-
console.error("[gtkx] Error handling request:", error);
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
private async handleRequest(request: IpcRequest): Promise<void> {
|
|
317
|
-
const { id, method, params } = request;
|
|
318
|
-
|
|
319
|
-
try {
|
|
320
|
-
const result = await this.executeMethod(method as IpcMethod, params);
|
|
321
|
-
this.send({ id, result });
|
|
322
|
-
} catch (error) {
|
|
323
|
-
if (error instanceof McpError) {
|
|
324
|
-
this.send({ id, error: error.toIpcError() });
|
|
325
|
-
} else {
|
|
326
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
327
|
-
this.send({
|
|
328
|
-
id,
|
|
329
|
-
error: {
|
|
330
|
-
code: McpErrorCode.INTERNAL_ERROR,
|
|
331
|
-
message,
|
|
332
|
-
},
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
private async executeMethod(method: IpcMethod, params: unknown): Promise<unknown> {
|
|
339
|
-
const app = Gio.Application.getDefault() as Gtk.Application | null;
|
|
340
|
-
|
|
341
|
-
if (!app) {
|
|
342
|
-
throw new Error("Application not initialized");
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
refreshWidgetRegistry();
|
|
346
|
-
|
|
347
|
-
switch (method) {
|
|
348
|
-
case "app.getWindows": {
|
|
349
|
-
const windows = Gtk.Window.listToplevels();
|
|
350
|
-
return {
|
|
351
|
-
windows: windows.map((w) => ({
|
|
352
|
-
id: getWidgetId(w),
|
|
353
|
-
title: (w as Gtk.Window).getTitle?.() ?? null,
|
|
354
|
-
})),
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
case "widget.getTree": {
|
|
359
|
-
const testing = await loadTestingModule();
|
|
360
|
-
return { tree: testing.prettyWidget(app, { includeIds: true, highlight: false }) };
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
case "widget.query": {
|
|
364
|
-
const testing = await loadTestingModule();
|
|
365
|
-
const p = params as { queryType: string; value: string | number; options?: Record<string, unknown> };
|
|
366
|
-
let widgets: Gtk.Widget[] = [];
|
|
367
|
-
|
|
368
|
-
switch (p.queryType) {
|
|
369
|
-
case "role": {
|
|
370
|
-
const roleValue =
|
|
371
|
-
typeof p.value === "string"
|
|
372
|
-
? (Gtk.AccessibleRole[p.value as keyof typeof Gtk.AccessibleRole] as Gtk.AccessibleRole)
|
|
373
|
-
: (p.value as Gtk.AccessibleRole);
|
|
374
|
-
widgets = await testing.findAllByRole(app, roleValue, p.options);
|
|
375
|
-
break;
|
|
376
|
-
}
|
|
377
|
-
case "text":
|
|
378
|
-
widgets = await testing.findAllByText(app, String(p.value), p.options);
|
|
379
|
-
break;
|
|
380
|
-
case "testId":
|
|
381
|
-
widgets = await testing.findAllByTestId(app, String(p.value), p.options);
|
|
382
|
-
break;
|
|
383
|
-
case "labelText":
|
|
384
|
-
widgets = await testing.findAllByLabelText(app, String(p.value), p.options);
|
|
385
|
-
break;
|
|
386
|
-
default:
|
|
387
|
-
throw new Error(`Unknown query type: ${p.queryType}`);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return { widgets: widgets.map((w) => serializeWidget(w)) };
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
case "widget.getProps": {
|
|
394
|
-
const p = params as { widgetId: string };
|
|
395
|
-
const widget = getWidgetById(p.widgetId);
|
|
396
|
-
if (!widget) {
|
|
397
|
-
throw widgetNotFoundError(p.widgetId);
|
|
398
|
-
}
|
|
399
|
-
return serializeWidget(widget);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
case "widget.click": {
|
|
403
|
-
const testing = await loadTestingModule();
|
|
404
|
-
const p = params as { widgetId: string };
|
|
405
|
-
const widget = getWidgetById(p.widgetId);
|
|
406
|
-
if (!widget) {
|
|
407
|
-
throw widgetNotFoundError(p.widgetId);
|
|
408
|
-
}
|
|
409
|
-
await testing.userEvent.click(widget);
|
|
410
|
-
return { success: true };
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
case "widget.type": {
|
|
414
|
-
const testing = await loadTestingModule();
|
|
415
|
-
const p = params as { widgetId: string; text: string; clear?: boolean };
|
|
416
|
-
const widget = getWidgetById(p.widgetId);
|
|
417
|
-
if (!widget) {
|
|
418
|
-
throw widgetNotFoundError(p.widgetId);
|
|
419
|
-
}
|
|
420
|
-
if (p.clear) {
|
|
421
|
-
await testing.userEvent.clear(widget);
|
|
422
|
-
}
|
|
423
|
-
await testing.userEvent.type(widget, p.text);
|
|
424
|
-
return { success: true };
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
case "widget.fireEvent": {
|
|
428
|
-
const testing = await loadTestingModule();
|
|
429
|
-
const p = params as {
|
|
430
|
-
widgetId: string;
|
|
431
|
-
signal: string;
|
|
432
|
-
args?: (unknown | { type: string; value: unknown })[];
|
|
433
|
-
};
|
|
434
|
-
const widget = getWidgetById(p.widgetId);
|
|
435
|
-
if (!widget) {
|
|
436
|
-
throw widgetNotFoundError(p.widgetId);
|
|
437
|
-
}
|
|
438
|
-
const signalArgs = (p.args ?? []).map((arg) => {
|
|
439
|
-
const isTypedArg = typeof arg === "object" && arg !== null && "type" in arg && "value" in arg;
|
|
440
|
-
const argType = isTypedArg ? (arg as { type: string }).type : typeof arg;
|
|
441
|
-
const argValue = isTypedArg ? (arg as { value: unknown }).value : arg;
|
|
442
|
-
|
|
443
|
-
switch (argType) {
|
|
444
|
-
case "boolean":
|
|
445
|
-
return Value.newFromBoolean(argValue as boolean);
|
|
446
|
-
case "int":
|
|
447
|
-
return Value.newFromInt(argValue as number);
|
|
448
|
-
case "uint":
|
|
449
|
-
return Value.newFromUint(argValue as number);
|
|
450
|
-
case "int64":
|
|
451
|
-
return Value.newFromInt64(argValue as number);
|
|
452
|
-
case "uint64":
|
|
453
|
-
return Value.newFromUint64(argValue as number);
|
|
454
|
-
case "float":
|
|
455
|
-
return Value.newFromFloat(argValue as number);
|
|
456
|
-
case "double":
|
|
457
|
-
case "number":
|
|
458
|
-
return Value.newFromDouble(argValue as number);
|
|
459
|
-
case "string":
|
|
460
|
-
return Value.newFromString(argValue as string | null);
|
|
461
|
-
default:
|
|
462
|
-
throw new McpError(McpErrorCode.INVALID_REQUEST, `Unknown argument type: ${argType}`);
|
|
463
|
-
}
|
|
464
|
-
});
|
|
465
|
-
await testing.fireEvent(widget, p.signal, ...signalArgs);
|
|
466
|
-
return { success: true };
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
case "widget.screenshot": {
|
|
470
|
-
const testing = await loadTestingModule();
|
|
471
|
-
const p = params as { windowId?: string };
|
|
472
|
-
|
|
473
|
-
let targetWindow: Gtk.Window;
|
|
474
|
-
|
|
475
|
-
if (p.windowId) {
|
|
476
|
-
const widget = getWidgetById(p.windowId);
|
|
477
|
-
if (!widget) {
|
|
478
|
-
throw widgetNotFoundError(p.windowId);
|
|
479
|
-
}
|
|
480
|
-
targetWindow = widget as Gtk.Window;
|
|
481
|
-
} else {
|
|
482
|
-
const windows = app.getWindows();
|
|
483
|
-
if (windows.length === 0) {
|
|
484
|
-
throw new Error("No windows available for screenshot");
|
|
485
|
-
}
|
|
486
|
-
targetWindow = windows[0] as Gtk.Window;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
const result = await testing.screenshot(targetWindow);
|
|
490
|
-
return { data: result.data, mimeType: result.mimeType };
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
default:
|
|
494
|
-
throw methodNotFoundError(method);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
let globalClient: McpClient | null = null;
|
|
500
|
-
|
|
501
|
-
export const startMcpClient = async (appId: string): Promise<McpClient> => {
|
|
502
|
-
if (globalClient) {
|
|
503
|
-
return globalClient;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
globalClient = new McpClient({ appId });
|
|
507
|
-
|
|
508
|
-
await globalClient.connect().catch(() => {});
|
|
509
|
-
|
|
510
|
-
return globalClient;
|
|
511
|
-
};
|
|
512
|
-
|
|
513
|
-
export const stopMcpClient = (): void => {
|
|
514
|
-
if (globalClient) {
|
|
515
|
-
globalClient.disconnect();
|
|
516
|
-
globalClient = null;
|
|
517
|
-
}
|
|
518
|
-
};
|
package/src/templates.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import ejs from "ejs";
|
|
4
|
-
import type { TestingOption } from "./create.js";
|
|
5
|
-
|
|
6
|
-
export type TemplateContext = {
|
|
7
|
-
name: string;
|
|
8
|
-
appId: string;
|
|
9
|
-
title: string;
|
|
10
|
-
testing: TestingOption;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const getTemplatesDir = (): string => {
|
|
14
|
-
return join(import.meta.dirname, "..", "templates");
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const renderTemplate = (templatePath: string, context: TemplateContext): string => {
|
|
18
|
-
const templateContent = readFileSync(templatePath, "utf-8");
|
|
19
|
-
return ejs.render(templateContent, context);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export const renderFile = (templateName: string, context: TemplateContext): string => {
|
|
23
|
-
const templatesDir = getTemplatesDir();
|
|
24
|
-
const templatePath = join(templatesDir, templateName);
|
|
25
|
-
return renderTemplate(templatePath, context);
|
|
26
|
-
};
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import type { Plugin } from "vite";
|
|
3
|
-
|
|
4
|
-
const ASSET_RE = /\.(png|jpe?g|gif|svg|webp|webm|mp4|ogg|mp3|wav|flac|aac|woff2?|eot|ttf|otf|ico|avif|data)$/i;
|
|
5
|
-
const CSS_RE = /\.css$/i;
|
|
6
|
-
const VIRTUAL_PREFIX = "\0gtkx:";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Vite plugin that resolves static asset imports to filesystem paths
|
|
10
|
-
* and handles CSS imports for GTK applications.
|
|
11
|
-
*
|
|
12
|
-
* **Non-CSS assets:** In dev mode, asset imports resolve to the absolute
|
|
13
|
-
* source file path. In build mode, Vite's built-in asset pipeline handles
|
|
14
|
-
* emission and hashing; the `renderBuiltUrl` config in the builder
|
|
15
|
-
* converts the URL to a filesystem path via `import.meta.url`.
|
|
16
|
-
*
|
|
17
|
-
* **CSS imports (`import "./style.css"`):** Transformed into a module that
|
|
18
|
-
* calls `injectGlobal` from `@gtkx/css` with the file's contents, injecting
|
|
19
|
-
* the styles into the GTK CSS provider at runtime.
|
|
20
|
-
*
|
|
21
|
-
* **CSS URL imports (`import path from "./style.css?url"`):** Handled by
|
|
22
|
-
* Vite's built-in `?url` mechanism, which emits the file as an asset and
|
|
23
|
-
* resolves it to a filesystem path via `renderBuiltUrl`.
|
|
24
|
-
*/
|
|
25
|
-
export function gtkxAssets(): Plugin {
|
|
26
|
-
let isBuild = false;
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
name: "gtkx:assets",
|
|
30
|
-
enforce: "pre",
|
|
31
|
-
|
|
32
|
-
config() {
|
|
33
|
-
return {
|
|
34
|
-
assetsInclude: [ASSET_RE],
|
|
35
|
-
};
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
configResolved(config) {
|
|
39
|
-
isBuild = config.command === "build";
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
async resolveId(source, importer, options) {
|
|
43
|
-
if (!CSS_RE.test(source)) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const resolved = await this.resolve(source, importer, { ...options, skipSelf: true });
|
|
48
|
-
if (!resolved || resolved.external) return;
|
|
49
|
-
|
|
50
|
-
return `${VIRTUAL_PREFIX + resolved.id}?inject`;
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
load(id) {
|
|
54
|
-
if (id.startsWith(VIRTUAL_PREFIX) && id.endsWith("?inject")) {
|
|
55
|
-
const filePath = id.slice(VIRTUAL_PREFIX.length, -"?inject".length);
|
|
56
|
-
const content = readFileSync(filePath, "utf-8");
|
|
57
|
-
return [`import { injectGlobal } from "@gtkx/css";`, `injectGlobal(${JSON.stringify(content)});`].join(
|
|
58
|
-
"\n",
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (isBuild || !ASSET_RE.test(id)) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return `export default ${JSON.stringify(id)};`;
|
|
67
|
-
},
|
|
68
|
-
};
|
|
69
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
|
-
import { arch, platform } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import type { Plugin } from "vite";
|
|
6
|
-
|
|
7
|
-
const LOAD_NATIVE_BINDING_RE = /function loadNativeBinding\(\) \{[\s\S]*?\n\}/;
|
|
8
|
-
const NODE_OS_IMPORT_RE = /import\s*\{[^}]*\}\s*from\s*["']node:os["'];?\n?/;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Vite plugin that embeds the native `.node` binary into the build output.
|
|
12
|
-
*
|
|
13
|
-
* During production builds, resolves the platform-specific `.node` binary,
|
|
14
|
-
* copies it into the output directory as `gtkx.node`, and transforms the
|
|
15
|
-
* `loadNativeBinding` function in `@gtkx/native` to load `./gtkx.node`
|
|
16
|
-
* directly. This makes the bundle self-contained with no `node_modules`
|
|
17
|
-
* dependency at runtime.
|
|
18
|
-
*
|
|
19
|
-
* @param root - Project root directory used to resolve native packages
|
|
20
|
-
*/
|
|
21
|
-
export function gtkxNative(root: string): Plugin {
|
|
22
|
-
const projectRequire = createRequire(join(root, "package.json"));
|
|
23
|
-
let nativeIndexPath: string;
|
|
24
|
-
|
|
25
|
-
return {
|
|
26
|
-
name: "gtkx:native",
|
|
27
|
-
enforce: "pre",
|
|
28
|
-
|
|
29
|
-
buildStart() {
|
|
30
|
-
const currentPlatform = platform();
|
|
31
|
-
const currentArch = arch();
|
|
32
|
-
const packageName = `@gtkx/native-linux-${currentArch}`;
|
|
33
|
-
|
|
34
|
-
if (currentPlatform !== "linux") {
|
|
35
|
-
throw new Error(`Unsupported build platform: ${currentPlatform}. Only Linux is supported.`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (currentArch !== "x64" && currentArch !== "arm64") {
|
|
39
|
-
throw new Error(`Unsupported build architecture: ${currentArch}. Only x64 and arm64 are supported.`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
nativeIndexPath = projectRequire.resolve("@gtkx/native");
|
|
43
|
-
|
|
44
|
-
const nodePath = projectRequire.resolve(`${packageName}/index.node`);
|
|
45
|
-
const source = readFileSync(nodePath);
|
|
46
|
-
|
|
47
|
-
this.emitFile({
|
|
48
|
-
type: "asset",
|
|
49
|
-
fileName: "gtkx.node",
|
|
50
|
-
source,
|
|
51
|
-
});
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
transform(code, id) {
|
|
55
|
-
if (id !== nativeIndexPath) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return code
|
|
60
|
-
.replace(NODE_OS_IMPORT_RE, "")
|
|
61
|
-
.replace(LOAD_NATIVE_BINDING_RE, 'function loadNativeBinding() { return require("./gtkx.node"); }');
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
}
|