@frontman-ai/astro 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +53 -0
  2. package/dist/cli.js +2744 -0
  3. package/package.json +66 -0
  4. package/src/FrontmanAstro.res +20 -0
  5. package/src/FrontmanAstro.res.mjs +36 -0
  6. package/src/FrontmanAstro__AstroBindings.res +46 -0
  7. package/src/FrontmanAstro__AstroBindings.res.mjs +2 -0
  8. package/src/FrontmanAstro__Config.res +85 -0
  9. package/src/FrontmanAstro__Config.res.mjs +44 -0
  10. package/src/FrontmanAstro__Integration.res +35 -0
  11. package/src/FrontmanAstro__Integration.res.mjs +36 -0
  12. package/src/FrontmanAstro__Middleware.res +149 -0
  13. package/src/FrontmanAstro__Middleware.res.mjs +141 -0
  14. package/src/FrontmanAstro__Server.res +196 -0
  15. package/src/FrontmanAstro__Server.res.mjs +241 -0
  16. package/src/FrontmanAstro__ToolRegistry.res +21 -0
  17. package/src/FrontmanAstro__ToolRegistry.res.mjs +41 -0
  18. package/src/FrontmanAstro__ToolbarApp.res +50 -0
  19. package/src/FrontmanAstro__ToolbarApp.res.mjs +39 -0
  20. package/src/cli/FrontmanAstro__Cli.res +126 -0
  21. package/src/cli/FrontmanAstro__Cli.res.mjs +180 -0
  22. package/src/cli/FrontmanAstro__Cli__AutoEdit.res +300 -0
  23. package/src/cli/FrontmanAstro__Cli__AutoEdit.res.mjs +266 -0
  24. package/src/cli/FrontmanAstro__Cli__Detect.res +298 -0
  25. package/src/cli/FrontmanAstro__Cli__Detect.res.mjs +345 -0
  26. package/src/cli/FrontmanAstro__Cli__Files.res +244 -0
  27. package/src/cli/FrontmanAstro__Cli__Files.res.mjs +321 -0
  28. package/src/cli/FrontmanAstro__Cli__Install.res +224 -0
  29. package/src/cli/FrontmanAstro__Cli__Install.res.mjs +194 -0
  30. package/src/cli/FrontmanAstro__Cli__Style.res +22 -0
  31. package/src/cli/FrontmanAstro__Cli__Style.res.mjs +61 -0
  32. package/src/cli/FrontmanAstro__Cli__Templates.res +226 -0
  33. package/src/cli/FrontmanAstro__Cli__Templates.res.mjs +237 -0
  34. package/src/cli/cli.mjs +3 -0
  35. package/src/tools/FrontmanAstro__Tool__GetPages.res +164 -0
  36. package/src/tools/FrontmanAstro__Tool__GetPages.res.mjs +180 -0
@@ -0,0 +1,196 @@
1
+ // Request handlers for Frontman Astro endpoints
2
+
3
+ module Protocol = FrontmanFrontmanProtocol
4
+ module MCP = Protocol.FrontmanProtocol__MCP
5
+ module Relay = Protocol.FrontmanProtocol__Relay
6
+ module Core = FrontmanFrontmanCore
7
+ module CoreServer = Core.FrontmanCore__Server
8
+ module CoreSSE = Core.FrontmanCore__SSE
9
+ module ToolRegistry = FrontmanAstro__ToolRegistry
10
+ module Config = FrontmanAstro__Config
11
+ module WebStreams = FrontmanBindings.WebStreams
12
+ module DOMElementToComponentSource = FrontmanBindings.DOMElementToComponentSource
13
+
14
+ // GET /frontman/tools
15
+ let handleGetTools = (~registry: ToolRegistry.t, ~config: Config.t): WebAPI.FetchAPI.response => {
16
+ let response = CoreServer.getToolsResponse(
17
+ ~registry,
18
+ ~serverName=config.serverName,
19
+ ~serverVersion=config.serverVersion,
20
+ )
21
+
22
+ let json = response->S.reverseConvertToJsonOrThrow(Relay.toolsResponseSchema)
23
+ let headers = WebAPI.HeadersInit.fromDict(Dict.fromArray([("Content-Type", "application/json")]))
24
+ WebAPI.Response.jsonR(~data=json, ~init={headers: headers})
25
+ }
26
+
27
+ // POST /frontman/tools/call - executes tool with SSE streaming
28
+ let handleToolCall = async (
29
+ ~registry: ToolRegistry.t,
30
+ ~config: Config.t,
31
+ req: WebAPI.FetchAPI.request,
32
+ ): WebAPI.FetchAPI.response => {
33
+ let body = await req->WebAPI.Request.json
34
+
35
+ let request = try {
36
+ Ok(body->S.parseOrThrow(Relay.toolCallRequestSchema))
37
+ } catch {
38
+ | S.Error(e) => Error(e.message)
39
+ }
40
+
41
+ switch request {
42
+ | Error(msg) =>
43
+ let errorResult: MCP.callToolResult = {
44
+ content: [{type_: "text", text: `Invalid request: ${msg}`}],
45
+ isError: Some(true),
46
+ }
47
+ let json = errorResult->S.reverseConvertToJsonOrThrow(MCP.callToolResultSchema)
48
+ WebAPI.Response.jsonR(~data=json, ~init={status: 400})
49
+
50
+ | Ok(request) =>
51
+ // Execute tool using core
52
+ let ctx: CoreServer.executionContext = {
53
+ projectRoot: config.projectRoot,
54
+ sourceRoot: config.sourceRoot,
55
+ onProgress: None,
56
+ }
57
+
58
+ let resultPromise = CoreServer.executeTool(
59
+ ~registry,
60
+ ~ctx,
61
+ ~name=request.name,
62
+ ~arguments=request.arguments,
63
+ )
64
+
65
+ let encoder = WebStreams.makeTextEncoder()
66
+ let stream = WebStreams.makeReadableStream({
67
+ start: controller => {
68
+ let _ = resultPromise->Promise.then(result => {
69
+ let mcpResult = CoreServer.resultToMCP(result)
70
+ let eventData = switch mcpResult.isError {
71
+ | Some(true) => CoreSSE.errorEvent(mcpResult)
72
+ | _ => CoreSSE.resultEvent(mcpResult)
73
+ }
74
+ controller->WebStreams.enqueue(encoder->WebStreams.encode(eventData))
75
+ controller->WebStreams.close
76
+ Promise.resolve()
77
+ })
78
+ },
79
+ })
80
+
81
+ WebAPI.Response.fromReadableStream(stream, ~init={headers: CoreSSE.headers()})
82
+ }
83
+ }
84
+
85
+ // Helper to convert absolute path to relative path (relative to sourceRoot)
86
+ // If the path starts with sourceRoot, strip it; otherwise return as-is
87
+ let toRelativePath = (absolutePath: string, sourceRoot: string): string => {
88
+ // Ensure sourceRoot ends with / for proper stripping
89
+ let normalizedRoot = if sourceRoot->String.endsWith("/") {
90
+ sourceRoot
91
+ } else {
92
+ sourceRoot ++ "/"
93
+ }
94
+
95
+ if absolutePath->String.startsWith(normalizedRoot) {
96
+ absolutePath->String.slice(~start=normalizedRoot->String.length, ~end=absolutePath->String.length)
97
+ } else if absolutePath->String.startsWith(sourceRoot) {
98
+ // Handle case where sourceRoot doesn't end with / but path matches exactly
99
+ absolutePath->String.slice(~start=sourceRoot->String.length, ~end=absolutePath->String.length)
100
+ } else {
101
+ absolutePath
102
+ }
103
+ }
104
+
105
+ // CORS headers for preflight requests
106
+ let corsHeaders = () => {
107
+ WebAPI.HeadersInit.fromDict(
108
+ Dict.fromArray([
109
+ ("Access-Control-Allow-Origin", "*"),
110
+ ("Access-Control-Allow-Methods", "GET, POST, OPTIONS"),
111
+ ("Access-Control-Allow-Headers", "Content-Type"),
112
+ ]),
113
+ )
114
+ }
115
+
116
+ // Handle CORS preflight
117
+ let handleCORS = (): WebAPI.FetchAPI.response => {
118
+ WebAPI.Response.fromNull(~init={status: 204, headers: corsHeaders()})
119
+ }
120
+
121
+ // POST /frontman/resolve-source-location - resolves source location via source maps
122
+ let handleResolveSourceLocation = async (
123
+ ~config: Config.t,
124
+ req: WebAPI.FetchAPI.request,
125
+ ): WebAPI.FetchAPI.response => {
126
+ let body = await req->WebAPI.Request.json
127
+
128
+ let requestObj = body->JSON.Decode.object
129
+
130
+ switch requestObj {
131
+ | None =>
132
+ WebAPI.Response.jsonR(
133
+ ~data=JSON.Encode.object(Dict.fromArray([("error", JSON.Encode.string("Invalid request body"))])),
134
+ ~init={status: 400},
135
+ )
136
+ | Some(obj) =>
137
+ let componentName = obj->Dict.get("componentName")->Option.flatMap(JSON.Decode.string)
138
+ let file = obj->Dict.get("file")->Option.flatMap(JSON.Decode.string)
139
+ let line = obj->Dict.get("line")->Option.flatMap(JSON.Decode.float)
140
+ let column = obj->Dict.get("column")->Option.flatMap(JSON.Decode.float)
141
+
142
+ switch (componentName, file, line, column) {
143
+ | (Some(componentName), Some(file), Some(line), Some(column)) =>
144
+ try {
145
+ let sourceLocation: DOMElementToComponentSource.sourceLocation = {
146
+ componentName,
147
+ file,
148
+ line: line->Float.toInt,
149
+ column: column->Float.toInt,
150
+ componentProps: None,
151
+ parent: None,
152
+ }
153
+
154
+ let resolved = await DOMElementToComponentSource.resolveSourceLocationInServer(sourceLocation)
155
+
156
+ // Convert absolute path to relative path (relative to sourceRoot)
157
+ // This ensures the agent can use the path directly with MCP tools
158
+ let relativeFile = toRelativePath(resolved.file, config.sourceRoot)
159
+
160
+ let responseJson = JSON.Encode.object(
161
+ Dict.fromArray([
162
+ ("componentName", JSON.Encode.string(resolved.componentName)),
163
+ ("file", JSON.Encode.string(relativeFile)),
164
+ ("line", JSON.Encode.float(resolved.line->Int.toFloat)),
165
+ ("column", JSON.Encode.float(resolved.column->Int.toFloat)),
166
+ ]),
167
+ )
168
+
169
+ let headers = WebAPI.HeadersInit.fromDict(Dict.fromArray([("Content-Type", "application/json")]))
170
+ WebAPI.Response.jsonR(~data=responseJson, ~init={headers: headers})
171
+ } catch {
172
+ | exn =>
173
+ let msg =
174
+ exn->JsExn.fromException->Option.flatMap(JsExn.message)->Option.getOr("Unknown error")
175
+ WebAPI.Response.jsonR(
176
+ ~data=JSON.Encode.object(
177
+ Dict.fromArray([
178
+ ("error", JSON.Encode.string("Failed to resolve source location")),
179
+ ("details", JSON.Encode.string(msg)),
180
+ ]),
181
+ ),
182
+ ~init={status: 500},
183
+ )
184
+ }
185
+ | _ =>
186
+ WebAPI.Response.jsonR(
187
+ ~data=JSON.Encode.object(
188
+ Dict.fromArray([
189
+ ("error", JSON.Encode.string("Missing required fields: componentName, file, line, column")),
190
+ ]),
191
+ ),
192
+ ~init={status: 400},
193
+ )
194
+ }
195
+ }
196
+ }
@@ -0,0 +1,241 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Web from "stream/web";
5
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
6
+ import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
7
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
8
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
9
+ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
10
+ import * as FrontmanCore__SSE$FrontmanFrontmanCore from "@frontman/frontman-core/src/FrontmanCore__SSE.res.mjs";
11
+ import * as Server from "dom-element-to-component-source/server";
12
+ import * as FrontmanCore__Server$FrontmanFrontmanCore from "@frontman/frontman-core/src/FrontmanCore__Server.res.mjs";
13
+ import * as FrontmanProtocol__MCP$FrontmanFrontmanProtocol from "@frontman/frontman-protocol/src/FrontmanProtocol__MCP.res.mjs";
14
+ import * as FrontmanProtocol__Relay$FrontmanFrontmanProtocol from "@frontman/frontman-protocol/src/FrontmanProtocol__Relay.res.mjs";
15
+
16
+ function handleGetTools(registry, config) {
17
+ let response = FrontmanCore__Server$FrontmanFrontmanCore.getToolsResponse(registry, config.serverName, config.serverVersion);
18
+ let json = S.reverseConvertToJsonOrThrow(response, FrontmanProtocol__Relay$FrontmanFrontmanProtocol.toolsResponseSchema);
19
+ let headers = Object.fromEntries([[
20
+ "Content-Type",
21
+ "application/json"
22
+ ]]);
23
+ return Response.json(json, {
24
+ headers: Primitive_option.some(headers)
25
+ });
26
+ }
27
+
28
+ async function handleToolCall(registry, config, req) {
29
+ let body = await req.json();
30
+ let request;
31
+ try {
32
+ request = {
33
+ TAG: "Ok",
34
+ _0: S.parseOrThrow(body, FrontmanProtocol__Relay$FrontmanFrontmanProtocol.toolCallRequestSchema)
35
+ };
36
+ } catch (raw_e) {
37
+ let e = Primitive_exceptions.internalToException(raw_e);
38
+ if (e.RE_EXN_ID === S.$$Error) {
39
+ request = {
40
+ TAG: "Error",
41
+ _0: e._1.message
42
+ };
43
+ } else {
44
+ throw e;
45
+ }
46
+ }
47
+ if (request.TAG === "Ok") {
48
+ let request$1 = request._0;
49
+ let ctx_projectRoot = config.projectRoot;
50
+ let ctx_sourceRoot = config.sourceRoot;
51
+ let ctx = {
52
+ projectRoot: ctx_projectRoot,
53
+ sourceRoot: ctx_sourceRoot,
54
+ onProgress: undefined
55
+ };
56
+ let resultPromise = FrontmanCore__Server$FrontmanFrontmanCore.executeTool(registry, ctx, request$1.name, request$1.arguments);
57
+ let encoder = new TextEncoder();
58
+ let stream = new Web.ReadableStream({
59
+ start: controller => {
60
+ resultPromise.then(result => {
61
+ let mcpResult = FrontmanCore__Server$FrontmanFrontmanCore.resultToMCP(result);
62
+ let match = mcpResult.isError;
63
+ let eventData = match !== undefined && match ? FrontmanCore__SSE$FrontmanFrontmanCore.errorEvent(mcpResult) : FrontmanCore__SSE$FrontmanFrontmanCore.resultEvent(mcpResult);
64
+ controller.enqueue(encoder.encode(eventData));
65
+ controller.close();
66
+ return Promise.resolve();
67
+ });
68
+ }
69
+ });
70
+ return new Response(stream, {
71
+ headers: Primitive_option.some(FrontmanCore__SSE$FrontmanFrontmanCore.headers())
72
+ });
73
+ }
74
+ let errorResult_content = [{
75
+ type: "text",
76
+ text: `Invalid request: ` + request._0
77
+ }];
78
+ let errorResult_isError = true;
79
+ let errorResult = {
80
+ content: errorResult_content,
81
+ isError: errorResult_isError
82
+ };
83
+ let json = S.reverseConvertToJsonOrThrow(errorResult, FrontmanProtocol__MCP$FrontmanFrontmanProtocol.callToolResultSchema);
84
+ return Response.json(json, {
85
+ status: 400
86
+ });
87
+ }
88
+
89
+ function toRelativePath(absolutePath, sourceRoot) {
90
+ let normalizedRoot = sourceRoot.endsWith("/") ? sourceRoot : sourceRoot + "/";
91
+ if (absolutePath.startsWith(normalizedRoot)) {
92
+ return absolutePath.slice(normalizedRoot.length, absolutePath.length);
93
+ } else if (absolutePath.startsWith(sourceRoot)) {
94
+ return absolutePath.slice(sourceRoot.length, absolutePath.length);
95
+ } else {
96
+ return absolutePath;
97
+ }
98
+ }
99
+
100
+ function corsHeaders() {
101
+ return Object.fromEntries([
102
+ [
103
+ "Access-Control-Allow-Origin",
104
+ "*"
105
+ ],
106
+ [
107
+ "Access-Control-Allow-Methods",
108
+ "GET, POST, OPTIONS"
109
+ ],
110
+ [
111
+ "Access-Control-Allow-Headers",
112
+ "Content-Type"
113
+ ]
114
+ ]);
115
+ }
116
+
117
+ function handleCORS() {
118
+ return new Response(null, {
119
+ status: 204,
120
+ headers: Primitive_option.some(corsHeaders())
121
+ });
122
+ }
123
+
124
+ async function handleResolveSourceLocation(config, req) {
125
+ let body = await req.json();
126
+ let requestObj = Stdlib_JSON.Decode.object(body);
127
+ if (requestObj === undefined) {
128
+ return Response.json(Object.fromEntries([[
129
+ "error",
130
+ "Invalid request body"
131
+ ]]), {
132
+ status: 400
133
+ });
134
+ }
135
+ let componentName = Stdlib_Option.flatMap(requestObj["componentName"], Stdlib_JSON.Decode.string);
136
+ let file = Stdlib_Option.flatMap(requestObj["file"], Stdlib_JSON.Decode.string);
137
+ let line = Stdlib_Option.flatMap(requestObj["line"], Stdlib_JSON.Decode.float);
138
+ let column = Stdlib_Option.flatMap(requestObj["column"], Stdlib_JSON.Decode.float);
139
+ if (componentName !== undefined && file !== undefined && line !== undefined && column !== undefined) {
140
+ try {
141
+ let sourceLocation_line = line | 0;
142
+ let sourceLocation_column = column | 0;
143
+ let sourceLocation = {
144
+ componentName: componentName,
145
+ file: file,
146
+ line: sourceLocation_line,
147
+ column: sourceLocation_column,
148
+ componentProps: undefined,
149
+ parent: undefined
150
+ };
151
+ let resolved = await Server.resolveSourceLocationInServer(sourceLocation);
152
+ let relativeFile = toRelativePath(resolved.file, config.sourceRoot);
153
+ let responseJson = Object.fromEntries([
154
+ [
155
+ "componentName",
156
+ resolved.componentName
157
+ ],
158
+ [
159
+ "file",
160
+ relativeFile
161
+ ],
162
+ [
163
+ "line",
164
+ resolved.line
165
+ ],
166
+ [
167
+ "column",
168
+ resolved.column
169
+ ]
170
+ ]);
171
+ let headers = Object.fromEntries([[
172
+ "Content-Type",
173
+ "application/json"
174
+ ]]);
175
+ return Response.json(responseJson, {
176
+ headers: Primitive_option.some(headers)
177
+ });
178
+ } catch (raw_exn) {
179
+ let exn = Primitive_exceptions.internalToException(raw_exn);
180
+ let msg = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(exn), Stdlib_JsExn.message), "Unknown error");
181
+ return Response.json(Object.fromEntries([
182
+ [
183
+ "error",
184
+ "Failed to resolve source location"
185
+ ],
186
+ [
187
+ "details",
188
+ msg
189
+ ]
190
+ ]), {
191
+ status: 500
192
+ });
193
+ }
194
+ }
195
+ return Response.json(Object.fromEntries([[
196
+ "error",
197
+ "Missing required fields: componentName, file, line, column"
198
+ ]]), {
199
+ status: 400
200
+ });
201
+ }
202
+
203
+ let Protocol;
204
+
205
+ let MCP;
206
+
207
+ let Relay;
208
+
209
+ let Core;
210
+
211
+ let CoreServer;
212
+
213
+ let CoreSSE;
214
+
215
+ let ToolRegistry;
216
+
217
+ let Config;
218
+
219
+ let WebStreams;
220
+
221
+ let DOMElementToComponentSource;
222
+
223
+ export {
224
+ Protocol,
225
+ MCP,
226
+ Relay,
227
+ Core,
228
+ CoreServer,
229
+ CoreSSE,
230
+ ToolRegistry,
231
+ Config,
232
+ WebStreams,
233
+ DOMElementToComponentSource,
234
+ handleGetTools,
235
+ handleToolCall,
236
+ toRelativePath,
237
+ corsHeaders,
238
+ handleCORS,
239
+ handleResolveSourceLocation,
240
+ }
241
+ /* S Not a pure module */
@@ -0,0 +1,21 @@
1
+ // Tool registry for Astro - composes core tools with Astro specific tools
2
+
3
+ module Core = FrontmanFrontmanCore
4
+ module CoreRegistry = Core.FrontmanCore__ToolRegistry
5
+
6
+ // Re-export types from core
7
+ type tool = CoreRegistry.tool
8
+ type t = CoreRegistry.t
9
+
10
+ // Astro specific tools
11
+ let astroTools: array<tool> = [module(FrontmanAstro__Tool__GetPages)]
12
+
13
+ let make = (): t => {
14
+ CoreRegistry.coreTools()->CoreRegistry.addTools(astroTools)
15
+ }
16
+
17
+ // Re-export functions from core
18
+ let getToolByName = CoreRegistry.getToolByName
19
+ let getToolDefinitions = CoreRegistry.getToolDefinitions
20
+ let addTools = CoreRegistry.addTools
21
+ let count = CoreRegistry.count
@@ -0,0 +1,41 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as FrontmanAstro__Tool__GetPages$FrontmanAiAstro from "./tools/FrontmanAstro__Tool__GetPages.res.mjs";
4
+ import * as FrontmanCore__ToolRegistry$FrontmanFrontmanCore from "@frontman/frontman-core/src/FrontmanCore__ToolRegistry.res.mjs";
5
+
6
+ let astroTools = [{
7
+ name: FrontmanAstro__Tool__GetPages$FrontmanAiAstro.name,
8
+ description: FrontmanAstro__Tool__GetPages$FrontmanAiAstro.description,
9
+ inputSchema: FrontmanAstro__Tool__GetPages$FrontmanAiAstro.inputSchema,
10
+ outputSchema: FrontmanAstro__Tool__GetPages$FrontmanAiAstro.outputSchema,
11
+ execute: FrontmanAstro__Tool__GetPages$FrontmanAiAstro.execute,
12
+ visibleToAgent: true
13
+ }];
14
+
15
+ function make() {
16
+ return FrontmanCore__ToolRegistry$FrontmanFrontmanCore.addTools(FrontmanCore__ToolRegistry$FrontmanFrontmanCore.coreTools(), astroTools);
17
+ }
18
+
19
+ let Core;
20
+
21
+ let CoreRegistry;
22
+
23
+ let getToolByName = FrontmanCore__ToolRegistry$FrontmanFrontmanCore.getToolByName;
24
+
25
+ let getToolDefinitions = FrontmanCore__ToolRegistry$FrontmanFrontmanCore.getToolDefinitions;
26
+
27
+ let addTools = FrontmanCore__ToolRegistry$FrontmanFrontmanCore.addTools;
28
+
29
+ let count = FrontmanCore__ToolRegistry$FrontmanFrontmanCore.count;
30
+
31
+ export {
32
+ Core,
33
+ CoreRegistry,
34
+ astroTools,
35
+ make,
36
+ getToolByName,
37
+ getToolDefinitions,
38
+ addTools,
39
+ count,
40
+ }
41
+ /* FrontmanAstro__Tool__GetPages-FrontmanAiAstro Not a pure module */
@@ -0,0 +1,50 @@
1
+ // Frontman Dev Toolbar App
2
+
3
+ module Bindings = FrontmanAstro__AstroBindings
4
+
5
+ // Type for the annotations API exposed on window
6
+ type annotation = {
7
+ file: string,
8
+ loc: string,
9
+ }
10
+
11
+ type annotationsApi = {
12
+ get: WebAPI.DOMAPI.element => option<annotation>,
13
+ has: WebAPI.DOMAPI.element => bool,
14
+ size: unit => int,
15
+ }
16
+
17
+ // External binding to window.__frontman_annotations__
18
+ @val @scope("window")
19
+ external annotations: option<annotationsApi> = "__frontman_annotations__"
20
+
21
+ // The toolbar app definition
22
+ let app: Bindings.toolbarAppConfig = {
23
+ init: (_canvas, _app, _server) => {
24
+ switch annotations {
25
+ | Some(api) if api.size() > 0 =>
26
+ Console.log(`[Frontman] SUCCESS - Captured ${api.size()->Int.toString} elements`)
27
+
28
+ // Test retrieval on first h1 or body child
29
+ let testEl =
30
+ WebAPI.Global.document->WebAPI.Document.querySelector("h1")->Null.toOption
31
+ switch testEl {
32
+ | Some(el) =>
33
+ switch api.get(el) {
34
+ | Some(data) =>
35
+ Console.log(`[Frontman] Test element: H1 -> ${data.file}:${data.loc}`)
36
+ | None =>
37
+ Console.log("[Frontman] Test element H1 has no annotation (might be in layout)")
38
+ }
39
+ | None => ()
40
+ }
41
+ | Some(_) =>
42
+ Console.log("[Frontman] WARNING - Annotations API exists but captured 0 elements")
43
+ | None =>
44
+ Console.log("[Frontman] FAILED - window.__frontman_annotations__ not found")
45
+ }
46
+ },
47
+ }
48
+
49
+ // Export as default for Astro to pick up
50
+ let default = Bindings.defineToolbarApp(app)
@@ -0,0 +1,39 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Toolbar from "astro/toolbar";
4
+
5
+ let app = {
6
+ init: (_canvas, _app, _server) => {
7
+ let api = window.__frontman_annotations__;
8
+ if (api !== undefined) {
9
+ if (api.size() > 0) {
10
+ console.log(`[Frontman] SUCCESS - Captured ` + api.size().toString() + ` elements`);
11
+ let testEl = document.querySelector("h1");
12
+ if (testEl === null) {
13
+ return;
14
+ }
15
+ let data = api.get(testEl);
16
+ if (data !== undefined) {
17
+ console.log(`[Frontman] Test element: H1 -> ` + data.file + `:` + data.loc);
18
+ } else {
19
+ console.log("[Frontman] Test element H1 has no annotation (might be in layout)");
20
+ }
21
+ return;
22
+ }
23
+ console.log("[Frontman] WARNING - Annotations API exists but captured 0 elements");
24
+ return;
25
+ }
26
+ console.log("[Frontman] FAILED - window.__frontman_annotations__ not found");
27
+ }
28
+ };
29
+
30
+ let $$default = Toolbar.defineToolbarApp(app);
31
+
32
+ let Bindings;
33
+
34
+ export {
35
+ Bindings,
36
+ app,
37
+ $$default as default,
38
+ }
39
+ /* default Not a pure module */