@frontman-ai/astro 0.1.2 → 0.1.3
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/dist/index.js +5727 -0
- package/{src/FrontmanAstro__Integration.res.mjs → dist/integration.js} +7 -18
- package/{src/FrontmanAstro__ToolbarApp.res.mjs → dist/toolbar.js} +8 -16
- package/package.json +11 -22
- package/src/FrontmanAstro.res +0 -20
- package/src/FrontmanAstro.res.mjs +0 -36
- package/src/FrontmanAstro__AstroBindings.res +0 -46
- package/src/FrontmanAstro__AstroBindings.res.mjs +0 -2
- package/src/FrontmanAstro__Config.res +0 -85
- package/src/FrontmanAstro__Config.res.mjs +0 -44
- package/src/FrontmanAstro__Integration.res +0 -35
- package/src/FrontmanAstro__Middleware.res +0 -149
- package/src/FrontmanAstro__Middleware.res.mjs +0 -141
- package/src/FrontmanAstro__Server.res +0 -196
- package/src/FrontmanAstro__Server.res.mjs +0 -241
- package/src/FrontmanAstro__ToolRegistry.res +0 -21
- package/src/FrontmanAstro__ToolRegistry.res.mjs +0 -41
- package/src/FrontmanAstro__ToolbarApp.res +0 -50
- package/src/cli/FrontmanAstro__Cli.res +0 -126
- package/src/cli/FrontmanAstro__Cli.res.mjs +0 -180
- package/src/cli/FrontmanAstro__Cli__AutoEdit.res +0 -300
- package/src/cli/FrontmanAstro__Cli__AutoEdit.res.mjs +0 -266
- package/src/cli/FrontmanAstro__Cli__Detect.res +0 -298
- package/src/cli/FrontmanAstro__Cli__Detect.res.mjs +0 -345
- package/src/cli/FrontmanAstro__Cli__Files.res +0 -244
- package/src/cli/FrontmanAstro__Cli__Files.res.mjs +0 -321
- package/src/cli/FrontmanAstro__Cli__Install.res +0 -224
- package/src/cli/FrontmanAstro__Cli__Install.res.mjs +0 -194
- package/src/cli/FrontmanAstro__Cli__Style.res +0 -22
- package/src/cli/FrontmanAstro__Cli__Style.res.mjs +0 -61
- package/src/cli/FrontmanAstro__Cli__Templates.res +0 -226
- package/src/cli/FrontmanAstro__Cli__Templates.res.mjs +0 -237
- package/src/cli/cli.mjs +0 -3
- package/src/tools/FrontmanAstro__Tool__GetPages.res +0 -164
- package/src/tools/FrontmanAstro__Tool__GetPages.res.mjs +0 -180
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
let icon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg>`;
|
|
5
|
-
|
|
1
|
+
// src/FrontmanAstro__Integration.res.mjs
|
|
2
|
+
var icon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg>`;
|
|
6
3
|
function getToolbarAppPath() {
|
|
7
|
-
return new URL("./
|
|
4
|
+
return new URL("./toolbar.js", import.meta.url).pathname;
|
|
8
5
|
}
|
|
9
|
-
|
|
10
6
|
function make() {
|
|
11
7
|
return {
|
|
12
8
|
name: "frontman",
|
|
13
9
|
hooks: {
|
|
14
|
-
"astro:config:setup": ctx => {
|
|
10
|
+
"astro:config:setup": (ctx) => {
|
|
15
11
|
if (ctx.command === "dev") {
|
|
16
12
|
return ctx.addDevToolbarApp({
|
|
17
13
|
id: "frontman:toolbar",
|
|
18
14
|
name: "Frontman",
|
|
19
|
-
icon
|
|
15
|
+
icon,
|
|
20
16
|
entrypoint: getToolbarAppPath()
|
|
21
17
|
});
|
|
22
18
|
}
|
|
@@ -24,13 +20,6 @@ function make() {
|
|
|
24
20
|
}
|
|
25
21
|
};
|
|
26
22
|
}
|
|
23
|
+
var Bindings;
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export {
|
|
31
|
-
Bindings,
|
|
32
|
-
icon,
|
|
33
|
-
getToolbarAppPath,
|
|
34
|
-
make,
|
|
35
|
-
}
|
|
36
|
-
/* No side effect */
|
|
25
|
+
export { Bindings, getToolbarAppPath, icon, make };
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import * as Toolbar from 'astro/toolbar';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
let app = {
|
|
3
|
+
// src/FrontmanAstro__ToolbarApp.res.mjs
|
|
4
|
+
var app = {
|
|
6
5
|
init: (_canvas, _app, _server) => {
|
|
7
6
|
let api = window.__frontman_annotations__;
|
|
8
|
-
if (api !==
|
|
7
|
+
if (api !== void 0) {
|
|
9
8
|
if (api.size() > 0) {
|
|
10
9
|
console.log(`[Frontman] SUCCESS - Captured ` + api.size().toString() + ` elements`);
|
|
11
10
|
let testEl = document.querySelector("h1");
|
|
@@ -13,7 +12,7 @@ let app = {
|
|
|
13
12
|
return;
|
|
14
13
|
}
|
|
15
14
|
let data = api.get(testEl);
|
|
16
|
-
if (data !==
|
|
15
|
+
if (data !== void 0) {
|
|
17
16
|
console.log(`[Frontman] Test element: H1 -> ` + data.file + `:` + data.loc);
|
|
18
17
|
} else {
|
|
19
18
|
console.log("[Frontman] Test element H1 has no annotation (might be in layout)");
|
|
@@ -26,14 +25,7 @@ let app = {
|
|
|
26
25
|
console.log("[Frontman] FAILED - window.__frontman_annotations__ not found");
|
|
27
26
|
}
|
|
28
27
|
};
|
|
28
|
+
var $$default = Toolbar.defineToolbarApp(app);
|
|
29
|
+
var Bindings;
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
let Bindings;
|
|
33
|
-
|
|
34
|
-
export {
|
|
35
|
-
Bindings,
|
|
36
|
-
app,
|
|
37
|
-
$$default as default,
|
|
38
|
-
}
|
|
39
|
-
/* default Not a pure module */
|
|
31
|
+
export { Bindings, app, $$default as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontman-ai/astro",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Astro integration for Frontman",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Frontman AI",
|
|
@@ -20,35 +20,24 @@
|
|
|
20
20
|
"development"
|
|
21
21
|
],
|
|
22
22
|
"type": "module",
|
|
23
|
-
"main": "./
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
24
|
"exports": {
|
|
25
|
-
".":
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
"./integration": {
|
|
30
|
-
"import": "./src/FrontmanAstro__Integration.res.mjs",
|
|
31
|
-
"default": "./src/FrontmanAstro__Integration.res.mjs"
|
|
32
|
-
},
|
|
33
|
-
"./toolbar": {
|
|
34
|
-
"import": "./src/FrontmanAstro__ToolbarApp.res.mjs",
|
|
35
|
-
"default": "./src/FrontmanAstro__ToolbarApp.res.mjs"
|
|
36
|
-
}
|
|
25
|
+
".": "./dist/index.js",
|
|
26
|
+
"./integration": "./dist/integration.js",
|
|
27
|
+
"./toolbar": "./dist/toolbar.js"
|
|
37
28
|
},
|
|
38
29
|
"bin": {
|
|
39
30
|
"frontman-ai-astro": "./dist/cli.js"
|
|
40
31
|
},
|
|
41
32
|
"files": [
|
|
42
|
-
"
|
|
43
|
-
"
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md"
|
|
44
35
|
],
|
|
45
36
|
"scripts": {
|
|
46
|
-
"build": "rescript build",
|
|
47
|
-
"build:
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
"dependencies": {
|
|
51
|
-
"@rescript/runtime": "12.0.0-beta.14"
|
|
37
|
+
"build:rescript": "rescript build",
|
|
38
|
+
"build:bundle": "tsup",
|
|
39
|
+
"build": "yarn build:rescript && yarn build:bundle",
|
|
40
|
+
"prepublishOnly": "yarn build"
|
|
52
41
|
},
|
|
53
42
|
"peerDependencies": {
|
|
54
43
|
"astro": "^5.0.0"
|
package/src/FrontmanAstro.res
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// Frontman Astro Integration - exposes framework tools via HTTP
|
|
2
|
-
// Used by FrontmanClient__Relay to execute file system operations
|
|
3
|
-
|
|
4
|
-
module Config = FrontmanAstro__Config
|
|
5
|
-
module Middleware = FrontmanAstro__Middleware
|
|
6
|
-
module Server = FrontmanAstro__Server
|
|
7
|
-
module ToolRegistry = FrontmanAstro__ToolRegistry
|
|
8
|
-
module Integration = FrontmanAstro__Integration
|
|
9
|
-
|
|
10
|
-
// Re-export core SSE for convenience
|
|
11
|
-
module SSE = FrontmanFrontmanCore.FrontmanCore__SSE
|
|
12
|
-
|
|
13
|
-
// Re-export for convenience
|
|
14
|
-
let createMiddleware = Middleware.createMiddleware
|
|
15
|
-
// makeConfig accepts an object with optional fields - JS-friendly API
|
|
16
|
-
let makeConfig = Config.makeFromObject
|
|
17
|
-
type config = Config.t
|
|
18
|
-
|
|
19
|
-
// Integration export
|
|
20
|
-
let frontmanIntegration = Integration.make
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as FrontmanAstro__Config$FrontmanAiAstro from "./FrontmanAstro__Config.res.mjs";
|
|
4
|
-
import * as FrontmanAstro__Middleware$FrontmanAiAstro from "./FrontmanAstro__Middleware.res.mjs";
|
|
5
|
-
import * as FrontmanAstro__Integration$FrontmanAiAstro from "./FrontmanAstro__Integration.res.mjs";
|
|
6
|
-
|
|
7
|
-
let Config;
|
|
8
|
-
|
|
9
|
-
let Middleware;
|
|
10
|
-
|
|
11
|
-
let Server;
|
|
12
|
-
|
|
13
|
-
let ToolRegistry;
|
|
14
|
-
|
|
15
|
-
let Integration;
|
|
16
|
-
|
|
17
|
-
let SSE;
|
|
18
|
-
|
|
19
|
-
let createMiddleware = FrontmanAstro__Middleware$FrontmanAiAstro.createMiddleware;
|
|
20
|
-
|
|
21
|
-
let makeConfig = FrontmanAstro__Config$FrontmanAiAstro.makeFromObject;
|
|
22
|
-
|
|
23
|
-
let frontmanIntegration = FrontmanAstro__Integration$FrontmanAiAstro.make;
|
|
24
|
-
|
|
25
|
-
export {
|
|
26
|
-
Config,
|
|
27
|
-
Middleware,
|
|
28
|
-
Server,
|
|
29
|
-
ToolRegistry,
|
|
30
|
-
Integration,
|
|
31
|
-
SSE,
|
|
32
|
-
createMiddleware,
|
|
33
|
-
makeConfig,
|
|
34
|
-
frontmanIntegration,
|
|
35
|
-
}
|
|
36
|
-
/* FrontmanAstro__Config-FrontmanAiAstro Not a pure module */
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
// Astro Integration API bindings
|
|
2
|
-
|
|
3
|
-
// Dev toolbar app configuration
|
|
4
|
-
// entrypoint: file path to the toolbar app module (string | URL supported, using string for simplicity)
|
|
5
|
-
type devToolbarAppConfig = {
|
|
6
|
-
id: string,
|
|
7
|
-
name: string,
|
|
8
|
-
icon: string,
|
|
9
|
-
entrypoint: string,
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Astro command type
|
|
13
|
-
type astroCommand = [#dev | #build | #preview | #sync]
|
|
14
|
-
|
|
15
|
-
// Hook context for astro:config:setup
|
|
16
|
-
type configSetupHookContext = {
|
|
17
|
-
addDevToolbarApp: devToolbarAppConfig => unit,
|
|
18
|
-
config: {root: string},
|
|
19
|
-
command: astroCommand,
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Astro integration hooks
|
|
23
|
-
type astroHooks = {
|
|
24
|
-
@as("astro:config:setup")
|
|
25
|
-
configSetup?: configSetupHookContext => unit,
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Astro integration type
|
|
29
|
-
type astroIntegration = {
|
|
30
|
-
name: string,
|
|
31
|
-
hooks: astroHooks,
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Toolbar app types
|
|
35
|
-
type toolbarCanvas // opaque
|
|
36
|
-
type toolbarApp // opaque
|
|
37
|
-
type toolbarServer // opaque
|
|
38
|
-
type toolbarAppDefinition // opaque - returned by defineToolbarApp
|
|
39
|
-
|
|
40
|
-
type toolbarAppConfig = {
|
|
41
|
-
init: (toolbarCanvas, toolbarApp, toolbarServer) => unit,
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// defineToolbarApp binding - returns an object that should be export default'd
|
|
45
|
-
@module("astro/toolbar")
|
|
46
|
-
external defineToolbarApp: toolbarAppConfig => toolbarAppDefinition = "defineToolbarApp"
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
// Astro configuration for Frontman
|
|
2
|
-
|
|
3
|
-
module Bindings = FrontmanBindings
|
|
4
|
-
|
|
5
|
-
// Default host can be overridden via FRONTMAN_HOST env var for remote development
|
|
6
|
-
let defaultHost = switch Bindings.Process.env->Dict.get("FRONTMAN_HOST") {
|
|
7
|
-
| Some(host) => host
|
|
8
|
-
| None => "frontman.local:4000"
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
type t = {
|
|
12
|
-
projectRoot: string,
|
|
13
|
-
// sourceRoot: root for resolving file paths from Astro's data-astro-source-file attributes
|
|
14
|
-
// In a monorepo, this is typically the monorepo root. Defaults to projectRoot.
|
|
15
|
-
sourceRoot: string,
|
|
16
|
-
basePath: string,
|
|
17
|
-
serverName: string,
|
|
18
|
-
serverVersion: string,
|
|
19
|
-
host: string,
|
|
20
|
-
clientUrl: string,
|
|
21
|
-
isLightTheme: bool,
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// JS-friendly type for config input
|
|
25
|
-
type jsConfigInput = {
|
|
26
|
-
projectRoot?: string,
|
|
27
|
-
sourceRoot?: string,
|
|
28
|
-
basePath?: string,
|
|
29
|
-
serverName?: string,
|
|
30
|
-
serverVersion?: string,
|
|
31
|
-
host?: string,
|
|
32
|
-
clientUrl?: string,
|
|
33
|
-
isLightTheme?: bool,
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// JS-friendly function that accepts a config object
|
|
37
|
-
// Use this from JavaScript/TypeScript: makeConfig({ projectRoot: "..." })
|
|
38
|
-
let makeFromObject = (config: jsConfigInput): t => {
|
|
39
|
-
let projectRoot =
|
|
40
|
-
config.projectRoot
|
|
41
|
-
->Option.orElse(
|
|
42
|
-
Bindings.Process.env
|
|
43
|
-
->Dict.get("PROJECT_ROOT")
|
|
44
|
-
->Option.orElse(Bindings.Process.env->Dict.get("PWD")),
|
|
45
|
-
)
|
|
46
|
-
->Option.getOr(".")
|
|
47
|
-
|
|
48
|
-
let sourceRoot = config.sourceRoot->Option.getOr(projectRoot)
|
|
49
|
-
let basePath = config.basePath->Option.getOr("frontman")
|
|
50
|
-
let serverName = config.serverName->Option.getOr("frontman-astro")
|
|
51
|
-
let serverVersion = config.serverVersion->Option.getOr("1.0.0")
|
|
52
|
-
let isLightTheme = config.isLightTheme->Option.getOr(false)
|
|
53
|
-
let host = config.host->Option.getOr(defaultHost)
|
|
54
|
-
|
|
55
|
-
let clientUrl = config.clientUrl->Option.getOr({
|
|
56
|
-
let baseUrl =
|
|
57
|
-
Bindings.Process.env
|
|
58
|
-
->Dict.get("FRONTMAN_CLIENT_URL")
|
|
59
|
-
->Option.getOr("http://localhost:5173/src/Main.res.mjs")
|
|
60
|
-
// Use URL API to properly append params (handles base URLs that already have query strings)
|
|
61
|
-
let url = WebAPI.URL.make(~url=baseUrl)
|
|
62
|
-
url.searchParams->WebAPI.URLSearchParams.set(~name="clientName", ~value="astro")
|
|
63
|
-
url.searchParams->WebAPI.URLSearchParams.set(~name="host", ~value=host)
|
|
64
|
-
url.href
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
// Assert clientUrl contains the required "host" query param that the client reads from import.meta.url
|
|
68
|
-
let parsedUrl = WebAPI.URL.make(~url=clientUrl)
|
|
69
|
-
if !(parsedUrl.searchParams->WebAPI.URLSearchParams.has(~name="host")) {
|
|
70
|
-
JsError.throwWithMessage(
|
|
71
|
-
`[frontman-astro] clientUrl must include a "host" query parameter. Got: ${clientUrl}`,
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
{
|
|
76
|
-
projectRoot,
|
|
77
|
-
sourceRoot,
|
|
78
|
-
basePath,
|
|
79
|
-
serverName,
|
|
80
|
-
serverVersion,
|
|
81
|
-
host,
|
|
82
|
-
clientUrl,
|
|
83
|
-
isLightTheme,
|
|
84
|
-
}
|
|
85
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
|
-
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
5
|
-
|
|
6
|
-
let host = process.env["FRONTMAN_HOST"];
|
|
7
|
-
|
|
8
|
-
let defaultHost = host !== undefined ? host : "frontman.local:4000";
|
|
9
|
-
|
|
10
|
-
function makeFromObject(config) {
|
|
11
|
-
let projectRoot = Stdlib_Option.getOr(Stdlib_Option.orElse(config.projectRoot, Stdlib_Option.orElse(process.env["PROJECT_ROOT"], process.env["PWD"])), ".");
|
|
12
|
-
let sourceRoot = Stdlib_Option.getOr(config.sourceRoot, projectRoot);
|
|
13
|
-
let basePath = Stdlib_Option.getOr(config.basePath, "frontman");
|
|
14
|
-
let serverName = Stdlib_Option.getOr(config.serverName, "frontman-astro");
|
|
15
|
-
let serverVersion = Stdlib_Option.getOr(config.serverVersion, "1.0.0");
|
|
16
|
-
let isLightTheme = Stdlib_Option.getOr(config.isLightTheme, false);
|
|
17
|
-
let host = Stdlib_Option.getOr(config.host, defaultHost);
|
|
18
|
-
let baseUrl = Stdlib_Option.getOr(process.env["FRONTMAN_CLIENT_URL"], "http://localhost:5173/src/Main.res.mjs");
|
|
19
|
-
let url = new URL(baseUrl);
|
|
20
|
-
let clientUrl = Stdlib_Option.getOr(config.clientUrl, (url.searchParams.set("clientName", "astro"), url.searchParams.set("host", host), url.href));
|
|
21
|
-
let parsedUrl = new URL(clientUrl);
|
|
22
|
-
if (!parsedUrl.searchParams.has("host")) {
|
|
23
|
-
Stdlib_JsError.throwWithMessage(`[frontman-astro] clientUrl must include a "host" query parameter. Got: ` + clientUrl);
|
|
24
|
-
}
|
|
25
|
-
return {
|
|
26
|
-
projectRoot: projectRoot,
|
|
27
|
-
sourceRoot: sourceRoot,
|
|
28
|
-
basePath: basePath,
|
|
29
|
-
serverName: serverName,
|
|
30
|
-
serverVersion: serverVersion,
|
|
31
|
-
host: host,
|
|
32
|
-
clientUrl: clientUrl,
|
|
33
|
-
isLightTheme: isLightTheme
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let Bindings;
|
|
38
|
-
|
|
39
|
-
export {
|
|
40
|
-
Bindings,
|
|
41
|
-
defaultHost,
|
|
42
|
-
makeFromObject,
|
|
43
|
-
}
|
|
44
|
-
/* host Not a pure module */
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// Frontman Astro Integration
|
|
2
|
-
|
|
3
|
-
module Bindings = FrontmanAstro__AstroBindings
|
|
4
|
-
|
|
5
|
-
// SVG icon for the toolbar
|
|
6
|
-
let icon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg>`
|
|
7
|
-
|
|
8
|
-
// Get the path to the toolbar app entrypoint
|
|
9
|
-
// Uses import.meta.url to resolve relative to this file
|
|
10
|
-
@val @scope(("import", "meta"))
|
|
11
|
-
external importMetaUrl: string = "url"
|
|
12
|
-
|
|
13
|
-
// Helper to create URL and get pathname
|
|
14
|
-
let getToolbarAppPath = () => {
|
|
15
|
-
let url = WebAPI.URL.make(~url="./FrontmanAstro__ToolbarApp.res.mjs", ~base=importMetaUrl)
|
|
16
|
-
url.pathname
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Create the Astro integration
|
|
20
|
-
let make = (): Bindings.astroIntegration => {
|
|
21
|
-
name: "frontman",
|
|
22
|
-
hooks: {
|
|
23
|
-
configSetup: ?Some(ctx => {
|
|
24
|
-
// Only add dev toolbar app in dev mode
|
|
25
|
-
if ctx.command == #dev {
|
|
26
|
-
ctx.addDevToolbarApp({
|
|
27
|
-
id: "frontman:toolbar",
|
|
28
|
-
name: "Frontman",
|
|
29
|
-
icon,
|
|
30
|
-
entrypoint: getToolbarAppPath(),
|
|
31
|
-
})
|
|
32
|
-
}
|
|
33
|
-
}),
|
|
34
|
-
},
|
|
35
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
// Astro middleware for Frontman
|
|
2
|
-
|
|
3
|
-
module Config = FrontmanAstro__Config
|
|
4
|
-
module Server = FrontmanAstro__Server
|
|
5
|
-
module ToolRegistry = FrontmanAstro__ToolRegistry
|
|
6
|
-
|
|
7
|
-
// Annotation capture script - injected before </body>
|
|
8
|
-
// Stores paths exactly as Astro provides them (should be absolute paths)
|
|
9
|
-
let annotationCaptureScript = `
|
|
10
|
-
<script>
|
|
11
|
-
(function() {
|
|
12
|
-
var annotations = new Map();
|
|
13
|
-
document.querySelectorAll('[data-astro-source-file]').forEach(function(el) {
|
|
14
|
-
annotations.set(el, {
|
|
15
|
-
file: el.getAttribute('data-astro-source-file'),
|
|
16
|
-
loc: el.getAttribute('data-astro-source-loc')
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
window.__frontman_annotations__ = {
|
|
20
|
-
_map: annotations,
|
|
21
|
-
get: function(el) { return annotations.get(el); },
|
|
22
|
-
has: function(el) { return annotations.has(el); },
|
|
23
|
-
size: function() { return annotations.size; }
|
|
24
|
-
};
|
|
25
|
-
console.log('[Frontman] Captured ' + annotations.size + ' elements');
|
|
26
|
-
})();
|
|
27
|
-
</script>
|
|
28
|
-
`
|
|
29
|
-
|
|
30
|
-
// Helper to inject script into HTML response
|
|
31
|
-
let injectAnnotationScript = async (response: WebAPI.FetchAPI.response): WebAPI.FetchAPI.response => {
|
|
32
|
-
let contentType = response.headers->WebAPI.Headers.get("content-type")->Null.toOption
|
|
33
|
-
|
|
34
|
-
switch contentType {
|
|
35
|
-
| Some(ct) if ct->String.includes("text/html") =>
|
|
36
|
-
let html = await response->WebAPI.Response.text
|
|
37
|
-
let injectedHtml = html->String.replace("</body>", `${annotationCaptureScript}</body>`)
|
|
38
|
-
|
|
39
|
-
WebAPI.Response.fromString(
|
|
40
|
-
injectedHtml,
|
|
41
|
-
~init={
|
|
42
|
-
status: response.status,
|
|
43
|
-
headers: WebAPI.HeadersInit.fromHeaders(response.headers),
|
|
44
|
-
},
|
|
45
|
-
)
|
|
46
|
-
| _ => response
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// HTML template for the Frontman UI
|
|
51
|
-
let uiHtml = (~clientUrl: string, ~isLightTheme: bool) => {
|
|
52
|
-
// Get the raw env var and filter out empty strings
|
|
53
|
-
let openrouterKey =
|
|
54
|
-
FrontmanBindings.Process.env
|
|
55
|
-
->Dict.get("OPENROUTER_API_KEY")
|
|
56
|
-
->Option.flatMap(key => key != "" ? Some(key) : None)
|
|
57
|
-
// Build JSON payload using proper JSON encoding to handle special characters
|
|
58
|
-
let configObj = Dict.fromArray([("framework", JSON.Encode.string("Astro"))])
|
|
59
|
-
openrouterKey->Option.forEach(key => {
|
|
60
|
-
configObj->Dict.set("openrouterKeyValue", JSON.Encode.string(key))
|
|
61
|
-
})
|
|
62
|
-
let runtimeConfig = JSON.stringify(JSON.Encode.object(configObj))
|
|
63
|
-
let themeClass = isLightTheme ? "" : "dark"
|
|
64
|
-
`<!DOCTYPE html>
|
|
65
|
-
<html lang="en" class="${themeClass}">
|
|
66
|
-
<head>
|
|
67
|
-
<meta charset="UTF-8">
|
|
68
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
69
|
-
<title>Frontman</title>
|
|
70
|
-
<style>
|
|
71
|
-
html, body, #root {
|
|
72
|
-
margin: 0;
|
|
73
|
-
padding: 0;
|
|
74
|
-
height: 100%;
|
|
75
|
-
width: 100%;
|
|
76
|
-
}
|
|
77
|
-
</style>
|
|
78
|
-
</head>
|
|
79
|
-
<body>
|
|
80
|
-
<div id="root"></div>
|
|
81
|
-
<script>window.__frontmanRuntime=${runtimeConfig}</script>
|
|
82
|
-
<script type="module" src="${clientUrl}"></script>
|
|
83
|
-
</body>
|
|
84
|
-
</html>`
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Serve UI HTML
|
|
88
|
-
let serveUI = (config: Config.t): WebAPI.FetchAPI.response => {
|
|
89
|
-
let html = uiHtml(~clientUrl=config.clientUrl, ~isLightTheme=config.isLightTheme)
|
|
90
|
-
let headers = WebAPI.HeadersInit.fromDict(Dict.fromArray([("Content-Type", "text/html")]))
|
|
91
|
-
WebAPI.Response.fromString(html, ~init={headers: headers})
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Type for Astro URL object (subset we need)
|
|
95
|
-
type astroUrl = {pathname: string}
|
|
96
|
-
|
|
97
|
-
// Type for Astro middleware context (subset of APIContext we actually use)
|
|
98
|
-
type astroContext = {
|
|
99
|
-
request: WebAPI.FetchAPI.request,
|
|
100
|
-
url: astroUrl,
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Type for Astro next function
|
|
104
|
-
type astroNext = unit => promise<WebAPI.FetchAPI.response>
|
|
105
|
-
|
|
106
|
-
// Create middleware handler
|
|
107
|
-
// Returns a function that can be used directly as Astro middleware
|
|
108
|
-
let createMiddleware = (config: Config.t) => {
|
|
109
|
-
let registry = ToolRegistry.make()
|
|
110
|
-
|
|
111
|
-
async (context: astroContext, next: astroNext): WebAPI.FetchAPI.response => {
|
|
112
|
-
let pathname = context.url.pathname
|
|
113
|
-
let method = context.request.method
|
|
114
|
-
|
|
115
|
-
let basePath = `/${config.basePath}`
|
|
116
|
-
|
|
117
|
-
// Check if this is a frontman route (exact match or subpath)
|
|
118
|
-
if !(pathname == basePath || pathname->String.startsWith(`${basePath}/`)) {
|
|
119
|
-
// Not a frontman route - pass through but inject script into HTML
|
|
120
|
-
let response = await next()
|
|
121
|
-
await injectAnnotationScript(response)
|
|
122
|
-
} else if method == "OPTIONS" {
|
|
123
|
-
// Handle CORS preflight
|
|
124
|
-
Server.handleCORS()
|
|
125
|
-
} else {
|
|
126
|
-
// Route handling
|
|
127
|
-
switch pathname {
|
|
128
|
-
| p if p == basePath || p == `${basePath}/` =>
|
|
129
|
-
serveUI(config)
|
|
130
|
-
|
|
131
|
-
| p if p == `${basePath}/tools` && method == "GET" =>
|
|
132
|
-
Server.handleGetTools(~registry, ~config)
|
|
133
|
-
|
|
134
|
-
| p if p == `${basePath}/tools/call` && method == "POST" =>
|
|
135
|
-
await Server.handleToolCall(~registry, ~config, context.request)
|
|
136
|
-
|
|
137
|
-
| p if p == `${basePath}/resolve-source-location` && method == "POST" =>
|
|
138
|
-
await Server.handleResolveSourceLocation(~config, context.request)
|
|
139
|
-
|
|
140
|
-
| _ =>
|
|
141
|
-
// Unknown frontman route
|
|
142
|
-
WebAPI.Response.jsonR(
|
|
143
|
-
~data=JSON.Encode.object(Dict.fromArray([("error", JSON.Encode.string("Not found"))])),
|
|
144
|
-
~init={status: 404},
|
|
145
|
-
)
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|