made-refine 0.1.13 → 0.2.1-beta.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.
package/dist/index.mjs CHANGED
@@ -2347,44 +2347,302 @@ function hslToRgb(h, s, l) {
2347
2347
  }
2348
2348
 
2349
2349
  // src/mcp-client.ts
2350
- var MCP_BASE = "http://127.0.0.1:4747";
2351
- var cachedToken = null;
2352
- async function getSessionToken(forceRefresh = false) {
2353
- if (!forceRefresh && cachedToken) return cachedToken;
2350
+ var PROTOCOL_VERSION = 1;
2351
+ var BOOTSTRAP_TIMEOUT_MS = 2500;
2352
+ var REQUEST_TIMEOUT_MS = 3e3;
2353
+ var SESSION_EXPIRY_SKEW_MS = 5e3;
2354
+ var CLIENT_NAME = "made-refine";
2355
+ var DEFAULT_CLIENT_VERSION = "unknown";
2356
+ var BOOTSTRAP_ENV_KEYS = [
2357
+ "MADE_REFINE_MCP_BOOTSTRAP_URL",
2358
+ "VITE_MADE_REFINE_MCP_BOOTSTRAP_URL",
2359
+ "NEXT_PUBLIC_MADE_REFINE_MCP_BOOTSTRAP_URL"
2360
+ ];
2361
+ var CLIENT_VERSION_ENV_KEYS = [
2362
+ "MADE_REFINE_VERSION",
2363
+ "VITE_MADE_REFINE_VERSION",
2364
+ "NEXT_PUBLIC_MADE_REFINE_VERSION"
2365
+ ];
2366
+ var cachedSession = null;
2367
+ function getTimeoutSignal(timeoutMs) {
2368
+ const timeout = AbortSignal.timeout;
2369
+ return typeof timeout === "function" ? timeout(timeoutMs) : void 0;
2370
+ }
2371
+ function getRuntimeMcpConfig() {
2372
+ if (typeof window === "undefined") return null;
2373
+ const config = window.__MADE_REFINE_CONFIG__;
2374
+ const bootstrapUrl = config?.mcp?.bootstrapUrl ?? config?.mcpBootstrapUrl ?? window.__MADE_REFINE_MCP_BOOTSTRAP_URL__;
2375
+ if (!config?.mcp && typeof bootstrapUrl !== "string") return null;
2376
+ return {
2377
+ ...config?.mcp ?? {},
2378
+ ...typeof bootstrapUrl === "string" ? { bootstrapUrl } : {}
2379
+ };
2380
+ }
2381
+ function getProcessEnvValue(keys) {
2382
+ const processLike = globalThis.process;
2383
+ const env = processLike?.env;
2384
+ if (!env) return null;
2385
+ for (const key of keys) {
2386
+ const value = env[key];
2387
+ if (typeof value === "string" && value.trim().length > 0) {
2388
+ return value.trim();
2389
+ }
2390
+ }
2391
+ return null;
2392
+ }
2393
+ function normalizeUrl(value) {
2394
+ if (typeof value !== "string") return null;
2395
+ const trimmed = value.trim();
2396
+ if (!trimmed) return null;
2397
+ if (/^https?:\/\//i.test(trimmed)) {
2398
+ return trimmed.replace(/\/+$/, "");
2399
+ }
2400
+ if (typeof window === "undefined" || !window.location?.origin) return null;
2354
2401
  try {
2355
- const res = await fetch(`${MCP_BASE}/api/health`, { signal: AbortSignal.timeout(2e3) });
2356
- if (!res.ok) return null;
2357
- const data = await res.json();
2358
- cachedToken = typeof data.sessionToken === "string" ? data.sessionToken : null;
2359
- return cachedToken;
2402
+ return new URL(trimmed, window.location.origin).toString().replace(/\/+$/, "");
2360
2403
  } catch {
2361
2404
  return null;
2362
2405
  }
2363
2406
  }
2364
- async function postWithSessionToken(path, payload) {
2365
- const send = async (token2) => {
2407
+ function normalizeBootstrapUrl(value) {
2408
+ const normalized = normalizeUrl(value);
2409
+ if (!normalized) return null;
2410
+ try {
2411
+ const url = new URL(normalized);
2412
+ const normalizedPathname = url.pathname.replace(/\/+$/, "");
2413
+ url.pathname = normalizedPathname.endsWith("/v1/bootstrap") ? normalizedPathname : `${normalizedPathname}/v1/bootstrap`;
2414
+ return url.toString();
2415
+ } catch {
2416
+ return normalized.endsWith("/v1/bootstrap") ? normalized : `${normalized}/v1/bootstrap`;
2417
+ }
2418
+ }
2419
+ function joinUrl(base, path) {
2420
+ return `${base.replace(/\/+$/, "")}${path}`;
2421
+ }
2422
+ function readString(record, key) {
2423
+ const value = record?.[key];
2424
+ if (typeof value !== "string") return null;
2425
+ const trimmed = value.trim();
2426
+ return trimmed.length > 0 ? trimmed : null;
2427
+ }
2428
+ function parseExpiresAt(value) {
2429
+ if (typeof value !== "string") return null;
2430
+ const timestamp = Date.parse(value);
2431
+ return Number.isFinite(timestamp) ? timestamp : null;
2432
+ }
2433
+ function readNumber(record, key) {
2434
+ const value = record?.[key];
2435
+ if (typeof value !== "number" || !Number.isFinite(value)) return null;
2436
+ return value;
2437
+ }
2438
+ function isLoopbackIpv4(hostname) {
2439
+ if (!/^127(?:\.\d{1,3}){3}$/.test(hostname)) return false;
2440
+ const segments = hostname.split(".");
2441
+ return segments.every((segment) => {
2442
+ const value = Number(segment);
2443
+ return Number.isInteger(value) && value >= 0 && value <= 255;
2444
+ });
2445
+ }
2446
+ function isLoopbackHostname(hostname) {
2447
+ if (hostname === "localhost" || hostname.endsWith(".localhost")) return true;
2448
+ if (hostname === "::1" || hostname === "[::1]") return true;
2449
+ return isLoopbackIpv4(hostname);
2450
+ }
2451
+ function isLoopbackHttpUrl(value) {
2452
+ try {
2453
+ const url = new URL(value);
2454
+ if (url.protocol !== "http:" && url.protocol !== "https:") return false;
2455
+ return isLoopbackHostname(url.hostname);
2456
+ } catch {
2457
+ return false;
2458
+ }
2459
+ }
2460
+ function buildBootstrapRequestBody(runtimeConfig) {
2461
+ const locationPath = typeof window !== "undefined" ? window.location.pathname : "";
2462
+ const locationOrigin = typeof window !== "undefined" ? window.location.origin : null;
2463
+ return {
2464
+ protocolVersion: PROTOCOL_VERSION,
2465
+ projectFingerprint: {
2466
+ path: runtimeConfig?.projectFingerprint?.path || locationPath || "unknown",
2467
+ gitRemoteHash: runtimeConfig?.projectFingerprint?.gitRemoteHash ?? null
2468
+ },
2469
+ ...runtimeConfig?.workspaceId ? {
2470
+ workspaceId: runtimeConfig.workspaceId
2471
+ } : {},
2472
+ client: {
2473
+ name: CLIENT_NAME,
2474
+ version: runtimeConfig?.clientVersion ?? getProcessEnvValue(CLIENT_VERSION_ENV_KEYS) ?? DEFAULT_CLIENT_VERSION,
2475
+ origin: locationOrigin
2476
+ }
2477
+ };
2478
+ }
2479
+ function resolveBootstrapUrl() {
2480
+ const runtimeConfig = getRuntimeMcpConfig();
2481
+ const runtimeUrl = normalizeBootstrapUrl(runtimeConfig?.bootstrapUrl);
2482
+ if (runtimeUrl) return runtimeUrl;
2483
+ const envUrl = normalizeBootstrapUrl(getProcessEnvValue(BOOTSTRAP_ENV_KEYS));
2484
+ if (envUrl) return envUrl;
2485
+ return null;
2486
+ }
2487
+ function isSessionUsable(session, bootstrapUrl) {
2488
+ if (!session) return false;
2489
+ if (session.bootstrapUrl !== bootstrapUrl) return false;
2490
+ if (session.expiresAt == null) return true;
2491
+ return Date.now() < session.expiresAt - SESSION_EXPIRY_SKEW_MS;
2492
+ }
2493
+ async function readJsonRecord(response) {
2494
+ try {
2495
+ const data = await response.json();
2496
+ if (!data || typeof data !== "object") return null;
2497
+ return data;
2498
+ } catch {
2499
+ return null;
2500
+ }
2501
+ }
2502
+ async function bootstrapSession(force = false) {
2503
+ const bootstrapUrl = resolveBootstrapUrl();
2504
+ if (!bootstrapUrl) {
2505
+ cachedSession = null;
2506
+ return null;
2507
+ }
2508
+ if (!force && isSessionUsable(cachedSession, bootstrapUrl)) {
2509
+ return cachedSession;
2510
+ }
2511
+ const runtimeConfig = getRuntimeMcpConfig();
2512
+ try {
2513
+ const response = await fetch(bootstrapUrl, {
2514
+ method: "POST",
2515
+ headers: { "Content-Type": "application/json" },
2516
+ body: JSON.stringify(buildBootstrapRequestBody(runtimeConfig)),
2517
+ signal: getTimeoutSignal(BOOTSTRAP_TIMEOUT_MS)
2518
+ });
2519
+ if (!response.ok) {
2520
+ cachedSession = null;
2521
+ return null;
2522
+ }
2523
+ const data = await readJsonRecord(response);
2524
+ const protocolVersion = readNumber(data, "protocolVersion");
2525
+ if (protocolVersion !== PROTOCOL_VERSION) {
2526
+ cachedSession = null;
2527
+ return null;
2528
+ }
2529
+ const ingestBaseUrl = normalizeUrl(readString(data, "ingestBaseUrl"));
2530
+ if (!ingestBaseUrl || !isLoopbackHttpUrl(ingestBaseUrl)) {
2531
+ cachedSession = null;
2532
+ return null;
2533
+ }
2534
+ const nextSession = {
2535
+ bootstrapUrl,
2536
+ ingestBaseUrl,
2537
+ serverInstanceId: readString(data, "serverInstanceId"),
2538
+ projectId: readString(data, "projectId"),
2539
+ sessionId: readString(data, "sessionId"),
2540
+ accessToken: readString(data, "accessToken"),
2541
+ expiresAt: parseExpiresAt(data?.expiresAt)
2542
+ };
2543
+ cachedSession = nextSession;
2544
+ return nextSession;
2545
+ } catch {
2546
+ cachedSession = null;
2547
+ return null;
2548
+ }
2549
+ }
2550
+ async function refreshSessionToken(session) {
2551
+ try {
2366
2552
  const headers = { "Content-Type": "application/json" };
2367
- if (token2) headers["X-Session-Token"] = token2;
2368
- return fetch(`${MCP_BASE}${path}`, {
2553
+ if (session.accessToken) {
2554
+ headers.Authorization = `Bearer ${session.accessToken}`;
2555
+ }
2556
+ const response = await fetch(joinUrl(session.ingestBaseUrl, "/v1/sessions/refresh"), {
2369
2557
  method: "POST",
2370
2558
  headers,
2371
- body: JSON.stringify(payload)
2559
+ body: JSON.stringify({
2560
+ protocolVersion: PROTOCOL_VERSION,
2561
+ projectId: session.projectId,
2562
+ sessionId: session.sessionId
2563
+ }),
2564
+ signal: getTimeoutSignal(REQUEST_TIMEOUT_MS)
2372
2565
  });
2566
+ if (response.status === 404 || response.status === 405) {
2567
+ return null;
2568
+ }
2569
+ if (!response.ok) {
2570
+ return null;
2571
+ }
2572
+ const data = await readJsonRecord(response);
2573
+ const nextToken = readString(data, "accessToken");
2574
+ if (!nextToken) {
2575
+ return null;
2576
+ }
2577
+ const refreshedSession = {
2578
+ ...session,
2579
+ accessToken: nextToken,
2580
+ expiresAt: parseExpiresAt(data?.expiresAt) ?? session.expiresAt
2581
+ };
2582
+ cachedSession = refreshedSession;
2583
+ return refreshedSession;
2584
+ } catch {
2585
+ return null;
2586
+ }
2587
+ }
2588
+ function createIdempotencyKey() {
2589
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
2590
+ return crypto.randomUUID();
2591
+ }
2592
+ const timestamp = Date.now().toString(36);
2593
+ const random = Math.random().toString(36).slice(2, 12);
2594
+ return `${timestamp}-${random}`;
2595
+ }
2596
+ async function sendAnnotationRequest(session, path, payload, idempotencyKey) {
2597
+ const headers = {
2598
+ "Content-Type": "application/json",
2599
+ "X-Idempotency-Key": idempotencyKey
2600
+ };
2601
+ if (session.accessToken) {
2602
+ headers.Authorization = `Bearer ${session.accessToken}`;
2603
+ }
2604
+ return fetch(joinUrl(session.ingestBaseUrl, path), {
2605
+ method: "POST",
2606
+ headers,
2607
+ body: JSON.stringify(payload),
2608
+ signal: getTimeoutSignal(REQUEST_TIMEOUT_MS)
2609
+ });
2610
+ }
2611
+ async function toClientResponse(response) {
2612
+ const data = await readJsonRecord(response);
2613
+ const bodyOk = data?.ok;
2614
+ const parsedOk = typeof bodyOk === "boolean" ? bodyOk : response.ok;
2615
+ return {
2616
+ ok: parsedOk && response.ok,
2617
+ id: readString(data, "id") ?? ""
2373
2618
  };
2374
- let token = await getSessionToken();
2375
- let res = await send(token);
2376
- if (res.status === 403) {
2377
- cachedToken = null;
2378
- token = await getSessionToken(true);
2379
- res = await send(token);
2619
+ }
2620
+ async function postWithSessionToken(path, payload) {
2621
+ const idempotencyKey = createIdempotencyKey();
2622
+ let session = await bootstrapSession();
2623
+ if (!session) return { ok: false, id: "" };
2624
+ let response;
2625
+ try {
2626
+ response = await sendAnnotationRequest(session, path, payload, idempotencyKey);
2627
+ } catch {
2628
+ return { ok: false, id: "" };
2629
+ }
2630
+ if (response.status === 401 || response.status === 403) {
2631
+ session = await refreshSessionToken(session) ?? await bootstrapSession(true);
2632
+ if (!session) return { ok: false, id: "" };
2633
+ try {
2634
+ response = await sendAnnotationRequest(session, path, payload, idempotencyKey);
2635
+ } catch {
2636
+ return { ok: false, id: "" };
2637
+ }
2380
2638
  }
2381
- return res.json();
2639
+ return toClientResponse(response);
2382
2640
  }
2383
2641
  async function sendEditToAgent(edit) {
2384
- return postWithSessionToken("/api/edit", edit);
2642
+ return postWithSessionToken("/v1/annotations/edit", edit);
2385
2643
  }
2386
2644
  async function sendCommentToAgent(comment) {
2387
- return postWithSessionToken("/api/comment", comment);
2645
+ return postWithSessionToken("/v1/annotations/comment", comment);
2388
2646
  }
2389
2647
 
2390
2648
  // src/provider.tsx
@@ -4050,7 +4308,7 @@ function MeasurementOverlay({
4050
4308
  children: [
4051
4309
  /* @__PURE__ */ jsx6(ElementHighlight, { element: selectedElement, color: BLUE }),
4052
4310
  hoveredElement && /* @__PURE__ */ jsx6(ElementHighlight, { element: hoveredElement, color: TOMATO, isDashed: true }),
4053
- measurements.map((line, i) => /* @__PURE__ */ jsx6(MeasurementLineComponent, { line }, i))
4311
+ measurements.map((line) => /* @__PURE__ */ jsx6(MeasurementLineComponent, { line }, `${line.direction}-${line.x1}-${line.y1}-${line.x2}-${line.y2}`))
4054
4312
  ]
4055
4313
  }
4056
4314
  );
@@ -4621,7 +4879,7 @@ function SelectionOverlay({
4621
4879
  onDoubleClick: handleDoubleClick,
4622
4880
  onMouseMove: handleMouseMove,
4623
4881
  onMouseLeave: handleMouseLeave,
4624
- children: moveHandleRects.map((targetRect, idx) => {
4882
+ children: moveHandleRects.map((targetRect) => {
4625
4883
  return /* @__PURE__ */ jsx8(
4626
4884
  "button",
4627
4885
  {
@@ -4648,7 +4906,7 @@ function SelectionOverlay({
4648
4906
  },
4649
4907
  onPointerDown: handleMoveHandlePointerDown(targetRect.target)
4650
4908
  },
4651
- `${idx}-${targetRect.left}-${targetRect.top}`
4909
+ `${targetRect.left}-${targetRect.top}-${targetRect.width}-${targetRect.height}`
4652
4910
  );
4653
4911
  })
4654
4912
  }
@@ -4791,10 +5049,12 @@ function CommentPin({
4791
5049
  }
4792
5050
  ),
4793
5051
  /* @__PURE__ */ jsx9(
4794
- "div",
5052
+ "button",
4795
5053
  {
5054
+ type: "button",
4796
5055
  "data-direct-edit": "comment-pin",
4797
- className: "group/pin fixed z-[99998] flex size-3 cursor-pointer items-center justify-center rounded-full bg-blue-500 shadow-md ring-2 ring-white transition-transform hover:scale-[1.67] hover:shadow-lg",
5056
+ "aria-label": `Comment ${index}`,
5057
+ className: "group/pin fixed z-[99998] flex size-3 cursor-pointer items-center justify-center rounded-full border-none bg-blue-500 p-0 shadow-md ring-2 ring-white transition-transform hover:scale-[1.67] hover:shadow-lg",
4798
5058
  style: {
4799
5059
  left: position.x - 6,
4800
5060
  top: position.y - 6,
@@ -4874,6 +5134,7 @@ function NewCommentInput({
4874
5134
  "div",
4875
5135
  {
4876
5136
  ref: cardRef,
5137
+ role: "presentation",
4877
5138
  "data-direct-edit": "comment-card",
4878
5139
  className: cn(
4879
5140
  "fixed z-[99999] flex items-center gap-1.5 rounded-xl outline outline-1 outline-foreground/10 bg-background p-1.5 shadow-lg",
@@ -4991,6 +5252,7 @@ function CommentThread({
4991
5252
  return /* @__PURE__ */ jsxs4(
4992
5253
  "div",
4993
5254
  {
5255
+ role: "presentation",
4994
5256
  "data-direct-edit": "comment-card",
4995
5257
  className: "fixed z-[99999] w-[280px] overflow-hidden rounded-xl outline outline-1 outline-foreground/10 bg-background shadow-lg",
4996
5258
  style: {
@@ -5088,13 +5350,13 @@ function CommentThread({
5088
5350
  ] }),
5089
5351
  /* @__PURE__ */ jsx9("p", { className: "text-xs leading-relaxed text-foreground", children: comment.text })
5090
5352
  ] }),
5091
- comment.replies.map((reply, i) => /* @__PURE__ */ jsxs4("div", { className: "border-t border-border/30 px-3 py-2.5", children: [
5353
+ comment.replies.map((reply) => /* @__PURE__ */ jsxs4("div", { className: "border-t border-border/30 px-3 py-2.5", children: [
5092
5354
  /* @__PURE__ */ jsxs4("div", { className: "mb-1 flex items-center gap-2", children: [
5093
5355
  /* @__PURE__ */ jsx9("div", { className: "flex size-5 shrink-0 items-center justify-center rounded-full bg-blue-500 text-[10px] font-bold text-white", children: index }),
5094
5356
  /* @__PURE__ */ jsx9("span", { className: "text-[10px] text-muted-foreground", children: formatRelativeTime(reply.createdAt) })
5095
5357
  ] }),
5096
5358
  /* @__PURE__ */ jsx9("p", { className: "text-xs leading-relaxed text-foreground", children: reply.text })
5097
- ] }, i))
5359
+ ] }, reply.createdAt))
5098
5360
  ] }),
5099
5361
  /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1.5 border-t border-border/50 px-2 py-1.5", children: [
5100
5362
  /* @__PURE__ */ jsx9(
@@ -7655,6 +7917,7 @@ function DirectEditPanelContent() {
7655
7917
  /* @__PURE__ */ jsx23(
7656
7918
  "div",
7657
7919
  {
7920
+ role: "presentation",
7658
7921
  "data-direct-edit": "overlay",
7659
7922
  className: cn("fixed inset-0 z-[99990] cursor-default"),
7660
7923
  style: { pointerEvents: textEditingElement ? "none" : "auto" },
@@ -7736,7 +7999,7 @@ function DirectEditPanelContent() {
7736
7999
  strokeWidth: 1,
7737
8000
  strokeDasharray: "4 2"
7738
8001
  },
7739
- i
8002
+ `${r.left}-${r.top}-${r.width}-${r.height}`
7740
8003
  );
7741
8004
  })
7742
8005
  ]
@@ -8978,10 +9241,19 @@ ${text}`);
8978
9241
  return /* @__PURE__ */ jsxs19(
8979
9242
  "div",
8980
9243
  {
9244
+ role: "button",
9245
+ tabIndex: 0,
8981
9246
  className: "group flex cursor-pointer items-start justify-between rounded-md px-1.5 py-1.5 text-xs transition-colors hover:bg-muted/50",
8982
9247
  onClick: () => {
8983
9248
  void handleCopyItem(item);
8984
9249
  },
9250
+ onKeyDown: (e) => {
9251
+ if (e.target !== e.currentTarget) return;
9252
+ if (e.key === "Enter" || e.key === " ") {
9253
+ e.preventDefault();
9254
+ void handleCopyItem(item);
9255
+ }
9256
+ },
8985
9257
  children: [
8986
9258
  /* @__PURE__ */ jsxs19("div", { className: "min-w-0 flex flex-1 flex-col items-start gap-[4px]", children: [
8987
9259
  /* @__PURE__ */ jsxs19(Badge, { variant: "secondary", className: "h-6 shrink-0 px-1.5 text-xs", children: [
@@ -9013,7 +9285,7 @@ ${text}`);
9013
9285
  )
9014
9286
  ]
9015
9287
  },
9016
- i
9288
+ item.type === "comment" ? item.comment.id : `edit-${i}`
9017
9289
  );
9018
9290
  }) })
9019
9291
  ]
@@ -9101,7 +9373,7 @@ ${text}`);
9101
9373
  { label: "Back / Exit", keys: ["Esc"] }
9102
9374
  ].map(({ label, keys }) => /* @__PURE__ */ jsxs19("div", { className: "flex h-8 w-full items-center justify-between rounded-md px-2 text-xs text-muted-foreground", children: [
9103
9375
  /* @__PURE__ */ jsx26("span", { children: label }),
9104
- /* @__PURE__ */ jsx26("span", { className: "flex items-center gap-0.5", children: keys.map((k, i) => /* @__PURE__ */ jsx26("kbd", { className: popupKbdClass, children: k }, i)) })
9376
+ /* @__PURE__ */ jsx26("span", { className: "flex items-center gap-0.5", children: keys.map((k, i) => /* @__PURE__ */ jsx26("kbd", { className: popupKbdClass, children: k }, typeof k === "string" ? k : i)) })
9105
9377
  ] }, label)) })
9106
9378
  ]
9107
9379
  }