@hyperframes/inspector 0.8.155 → 0.8.157

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Recover original HTML/CSS/GSAP context for **known elements before an edit**. This is not a video check, screenshot, diff, or affected-component verifier. It combines the I002 runtime/compiler provenance approach with the I003 HF-owned service boundary. The agent adapter holds no DOM or animation state.
4
4
 
5
- This source-only experimental package is private. It does not modify the shared installation or register itself in a harness. Base: HF 0.8.127, commit `b3d2b82fe`.
5
+ Published as `@hyperframes/inspector`. It does not modify the shared installation or register itself in a harness.
6
6
 
7
7
  ## Start
8
8
 
@@ -17,13 +17,9 @@ export HF_INSPECT_CHROME=/absolute/path/to/chrome
17
17
  bun packages/inspector/src/server.ts /absolute/video/project
18
18
  ```
19
19
 
20
- The service prints its loopback URL. Starting it does **not** start a browser. First inspection loads the project once. Configure the harness's MCP server to execute:
20
+ The service prints its loopback URL. Starting it does **not** start a browser. First inspection loads the project once.
21
21
 
22
- ```sh
23
- HF_INSPECT_URL=http://127.0.0.1:PRINTED_PORT bun packages/inspector/src/mcp.ts
24
- ```
25
-
26
- Tool: `inspect_components`. Direct HTTP equivalent: `POST /inspect` with the same JSON. The service is bound to one project at startup, so no per-call project path is needed. `GET /info` returns project identity and startup/query counters. Stop with SIGINT/SIGTERM; the service disposes its own pages/browser.
22
+ The package ships the capability, not agent tools: a harness defines its own tool names, descriptions and schemas, and calls `POST /inspect` with the JSON below. The service is bound to one project at startup, so no per-call project path is needed. `GET /info` returns project identity and startup/query counters. Stop with SIGINT/SIGTERM; the service disposes its own pages/browser.
27
23
 
28
24
  ## Input
29
25
 
@@ -83,7 +79,25 @@ Code blocks are atomic. `omitted`, `omittedCount`, and `nextOffset` describe mis
83
79
 
84
80
  `maxBytes` can be raised explicitly (2,048–256,000). A block larger than the budget is not cut in half: request that ID with a larger budget or read its file/range. Pagination can therefore remain at the same offset until the budget increases. Old-generation continuations are rejected.
85
81
 
86
- Full runtime evidence is only available by an explicit HTTP request with `debug: true`; it is deliberately not part of the agent tool schema or its default output budget.
82
+ Full runtime evidence is only available by an explicit HTTP request with `debug: true`; it is deliberately not part of the default request or its output budget.
83
+
84
+ ## Timeline
85
+
86
+ `POST /timeline` answers which animations are scheduled when, from the same browser, page and generation as `/inspect`. It never seeks, renders or edits files.
87
+
88
+ ```json
89
+ { "component": { "selector": "#title" }, "boundary": 7.9 }
90
+ ```
91
+
92
+ `component` is optional and resolves like an `/inspect` target; omit it for every registered HF timeline. Give at most one of `at`, `range: [start, end]` or `boundary`, in global seconds; omit all three for the schedule overview. Results are short text grouped by component, with event times, targets, properties and source locations; `generation` and `offset` page through them. Coverage is registered DOM-target GSAP animations and explicit HF items only; anything outside that, or past the expansion limits, is reported as incomplete coverage rather than guessed.
93
+
94
+ ## Element map
95
+
96
+ `POST /element-map` returns the composition's element map from the same page. It reads no query fields. A project with no `index.html` answers `404` `{ "error": "no-composition", "code": "no-composition" }`.
97
+
98
+ ## Errors
99
+
100
+ Every refusal is JSON `{ "error", "code" }`. `code` is the stable token (`no-composition`, `stale_generation`, `source_changed_during_query`, `request_too_large`); a sentence with no token is `invalid_request`. A missing browser is `503` `browser_unavailable`, and an unexpected failure is `500` `internal_error`. Importing the module does not listen; `createInspectorServer(project)` builds one. An unknown route is `404` `{ "error": "not_found", "code": "not_found" }`. `GET /info` adds `version`, this package's version. The process listens on `127.0.0.1` and refuses a `Host` that is not loopback, so a rebinding request cannot read the project path. Install the server with the package: `hyperframes-inspector <project> [port]`, or `node dist/server.js`.
87
101
 
88
102
  ## Lifecycle and overhead
89
103
 
@@ -10,16 +10,32 @@
10
10
  }
11
11
  return unknown();
12
12
  }
13
- function animationValues(vars, properties) {
14
- const startAt = vars.startAt && typeof vars.startAt === "object" ? vars.startAt : {};
13
+ var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
14
+ function declaredEase(ease) {
15
+ return typeof ease === "function" ? { kind: "expression", value: "custom function" } : declared(ease);
16
+ }
17
+ function animationValues(vars, properties, keyframesEase) {
18
+ const startAt = record(vars.startAt);
19
+ const endpoints = (property, end, start) => ({
20
+ property,
21
+ from: declared(vars.runBackwards ? end : start),
22
+ to: vars.runBackwards ? unknown() : declared(end)
23
+ });
24
+ const segment = declaredEase(vars.ease);
25
+ const whole = keyframesEase === void 0 ? void 0 : declaredEase(keyframesEase);
15
26
  return {
16
27
  basis: "authored-tween",
17
- properties: properties.map((property) => ({
18
- property,
19
- from: declared(vars.runBackwards ? vars[property] : startAt[property]),
20
- to: vars.runBackwards ? unknown() : declared(vars[property])
21
- })),
22
- ease: declared(vars.ease)
28
+ properties: properties.flatMap((property) => {
29
+ const nested = record(vars[property]);
30
+ const keys = property === "attr" || property === "css" ? Object.keys(nested) : [];
31
+ if (!keys.length) return [endpoints(property, vars[property], startAt[property])];
32
+ const nestedStart = record(startAt[property]);
33
+ return keys.map((key) => endpoints(`${property}.${key}`, nested[key], nestedStart[key]));
34
+ }),
35
+ ease: whole ? {
36
+ kind: "expression",
37
+ value: `${whole.value ?? "?"} across keyframes, ${segment.value ?? "?"} per segment`
38
+ } : segment
23
39
  };
24
40
  }
25
41
 
@@ -1610,7 +1626,8 @@
1610
1626
  const seen = /* @__PURE__ */ new Set();
1611
1627
  const resolver = createRuntimeStartTimeResolver({
1612
1628
  timelineRegistry: registry,
1613
- documentRef: scope.ownerDocument
1629
+ documentRef: scope.ownerDocument,
1630
+ includeAuthoredTimingAttrs: true
1614
1631
  });
1615
1632
  const rootElement = findRootCompositionElement(scope.ownerDocument);
1616
1633
  const clocks = /* @__PURE__ */ new Map();
@@ -1665,7 +1682,11 @@
1665
1682
  const properties = Object.keys(piece.vars ?? {}).filter(
1666
1683
  (k) => !reserved2.has(k) && !k.startsWith("on")
1667
1684
  );
1668
- const values = animationValues(piece.vars ?? {}, properties);
1685
+ const values = animationValues(
1686
+ piece.vars ?? {},
1687
+ properties,
1688
+ internal && a.vars?.keyframes ? a.vars.ease : void 0
1689
+ );
1669
1690
  const localIntervals = internal ? globalIntervals(
1670
1691
  a,
1671
1692
  clock,
@@ -1782,15 +1803,22 @@
1782
1803
  }
1783
1804
  return value2;
1784
1805
  };
1806
+ const ancestorId = (el) => {
1807
+ for (let parent = el.parentElement; parent; parent = parent.parentElement) {
1808
+ if (parent.id) return parent.id;
1809
+ }
1810
+ return "";
1811
+ };
1785
1812
  const describe = (el) => ({
1786
1813
  ref: ref(el),
1787
1814
  tag: el.tagName.toLowerCase(),
1788
1815
  id: el.id,
1789
1816
  hfId: el.getAttribute("data-hf-id"),
1790
1817
  classes: [...el.classList],
1791
- composition: el.closest("[data-composition-id]")?.getAttribute("data-composition-id") ?? null
1818
+ composition: el.closest("[data-composition-id]")?.getAttribute("data-composition-id") ?? null,
1819
+ withinId: ancestorId(el)
1792
1820
  });
1793
- function record(value2, stack) {
1821
+ function record2(value2, stack) {
1794
1822
  if (animation(value2) && !records.has(value2) && count < limit) {
1795
1823
  records.set(value2, stack);
1796
1824
  count++;
@@ -1816,8 +1844,8 @@
1816
1844
  if (name === "add" && animation(args[0])) attachments.set(args[0], stack);
1817
1845
  else if (before) {
1818
1846
  for (const child of self?.getChildren?.(false, true, true) ?? [])
1819
- if (!before.has(child)) record(child, stack);
1820
- } else record(result, stack);
1847
+ if (!before.has(child)) record2(child, stack);
1848
+ } else record2(result, stack);
1821
1849
  }
1822
1850
  return result;
1823
1851
  };