@agent-native/core 0.157.7 → 0.157.9

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 (69) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +26 -8
  3. package/corpus/templates/analytics/scripts/first-party-analytics-postgres-retention-dry-run.sql +258 -0
  4. package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +59 -27
  5. package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +4 -1
  6. package/corpus/templates/slides/actions/export-pptx.ts +261 -71
  7. package/corpus/templates/slides/actions/import-file.ts +12 -0
  8. package/corpus/templates/slides/actions/import-pptx.ts +9 -0
  9. package/corpus/templates/slides/app/lib/export-pptx-client.ts +45 -5
  10. package/corpus/templates/slides/server/handlers/import/html-converter.ts +69 -1
  11. package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +316 -64
  12. package/corpus/templates/slides/server/lib/source-import.ts +10 -1
  13. package/dist/agent/harness/migrations.d.ts +4 -0
  14. package/dist/agent/harness/migrations.js +39 -0
  15. package/dist/agent/production-agent.d.ts +2 -0
  16. package/dist/agent/production-agent.js +4 -0
  17. package/dist/agent/run-loop-with-resume.d.ts +1 -0
  18. package/dist/agent/run-loop-with-resume.js +45 -30
  19. package/dist/agent/run-migrations.d.ts +8 -0
  20. package/dist/agent/run-migrations.js +61 -0
  21. package/dist/chat-threads/migrations.d.ts +1 -0
  22. package/dist/chat-threads/migrations.js +1 -0
  23. package/dist/chat-threads/schema-migrations.d.ts +8 -0
  24. package/dist/chat-threads/schema-migrations.js +60 -0
  25. package/dist/client/AssistantChat.js +4 -0
  26. package/dist/client/chat/message-components.d.ts +2 -0
  27. package/dist/client/chat/message-components.js +11 -1
  28. package/dist/client/observability/ThumbsFeedback.js +35 -22
  29. package/dist/client/observability/useObservability.d.ts +1 -1
  30. package/dist/client/observability/useObservability.js +6 -3
  31. package/dist/collab/awareness.d.ts +2 -2
  32. package/dist/collab/struct-routes.d.ts +1 -1
  33. package/dist/feature-flags/a2a-action-route.js +4 -2
  34. package/dist/feature-flags/permissions.d.ts +2 -0
  35. package/dist/feature-flags/permissions.js +6 -1
  36. package/dist/ingestion/pptx.d.ts +14 -1
  37. package/dist/ingestion/pptx.js +222 -41
  38. package/dist/localization/core-messages/ar-SA.js +4 -0
  39. package/dist/localization/core-messages/de-DE.js +4 -0
  40. package/dist/localization/core-messages/en-US.d.ts +4 -0
  41. package/dist/localization/core-messages/en-US.js +4 -0
  42. package/dist/localization/core-messages/es-ES.js +4 -0
  43. package/dist/localization/core-messages/fr-FR.js +4 -0
  44. package/dist/localization/core-messages/hi-IN.js +4 -0
  45. package/dist/localization/core-messages/ja-JP.js +4 -0
  46. package/dist/localization/core-messages/ko-KR.js +4 -0
  47. package/dist/localization/core-messages/pt-BR.js +4 -0
  48. package/dist/localization/core-messages/zh-CN.js +4 -0
  49. package/dist/localization/core-messages/zh-TW.js +4 -0
  50. package/dist/localization/core-messages.d.ts +4 -0
  51. package/dist/notifications/routes.d.ts +1 -1
  52. package/dist/observability/feedback.js +40 -16
  53. package/dist/observability/routes.d.ts +1 -1
  54. package/dist/observability/routes.js +15 -3
  55. package/dist/observability/store.js +2 -0
  56. package/dist/observability/traces.js +5 -1
  57. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  58. package/dist/provider-api/actions/provider-api.d.ts +10 -10
  59. package/dist/resources/handlers.d.ts +1 -1
  60. package/dist/server/action-routes.d.ts +7 -5
  61. package/dist/server/action-routes.js +9 -4
  62. package/dist/server/better-auth-instance.js +4 -0
  63. package/dist/server/poll.d.ts +4 -4
  64. package/dist/server/poll.js +45 -13
  65. package/dist/server/realtime-voice.js +2 -1
  66. package/dist/server/release-migrations.js +16 -0
  67. package/dist/usage/migrations.d.ts +4 -0
  68. package/dist/usage/migrations.js +45 -0
  69. package/package.json +3 -3
@@ -13,6 +13,11 @@ function productionAdminAllowlist() {
13
13
  .map((email) => email.trim().toLowerCase())
14
14
  .filter(Boolean));
15
15
  }
16
+ /** Return whether an email is explicitly allowed to manage no-org flags. */
17
+ export function isFeatureFlagAdminEmail(email) {
18
+ const normalized = email?.trim().toLowerCase();
19
+ return Boolean(normalized && productionAdminAllowlist().has(normalized));
20
+ }
16
21
  /** Require an org admin/owner, or an explicit production no-org allowlist entry. */
17
22
  export async function requireFeatureFlagManager(scope) {
18
23
  const email = scope.userEmail?.trim().toLowerCase();
@@ -29,7 +34,7 @@ export async function requireFeatureFlagManager(scope) {
29
34
  }
30
35
  if (process.env.NODE_ENV !== "production")
31
36
  return { email, orgId: null };
32
- if (!productionAdminAllowlist().has(email)) {
37
+ if (!isFeatureFlagAdminEmail(email)) {
33
38
  throw new FeatureFlagPermissionError("Feature flag management without an organization requires an explicit admin allowlist entry.", 403);
34
39
  }
35
40
  return { email, orgId: null };
@@ -26,7 +26,7 @@ export interface ParsedPptxElement {
26
26
  id: string;
27
27
  name?: string;
28
28
  placeholderType?: string;
29
- kind: "text" | "image" | "shape";
29
+ kind: "text" | "image" | "shape" | "table";
30
30
  x: number;
31
31
  y: number;
32
32
  width: number;
@@ -45,6 +45,17 @@ export interface ParsedPptxElement {
45
45
  verticalAlign?: "top" | "middle" | "bottom";
46
46
  paragraphs?: ParsedPptxParagraph[];
47
47
  image?: ParsedPptxImage;
48
+ table?: ParsedPptxTable;
49
+ }
50
+ /** A `p:graphicFrame`'s `a:tbl` flattened into a simple row/cell grid. `hMerge`/`vMerge` continuation cells are omitted — their content is already represented once, by the spanning cell's `colSpan`/`rowSpan`. */
51
+ export interface ParsedPptxTable {
52
+ rows: ParsedPptxTableCell[][];
53
+ }
54
+ export interface ParsedPptxTableCell {
55
+ paragraphs: ParsedPptxParagraph[];
56
+ colSpan?: number;
57
+ rowSpan?: number;
58
+ fill?: string;
48
59
  }
49
60
  export type ParsedPptxTransition = "instant" | "none" | "fade" | "slide" | "zoom";
50
61
  export interface ParsedPptxImage {
@@ -75,6 +86,8 @@ export interface ParsedPptxSlide {
75
86
  layoutHint?: string;
76
87
  transition?: ParsedPptxTransition;
77
88
  splitByParagraph?: boolean;
89
+ /** Count of this slide's `graphicFrame` shapes that could not be converted into a `"table"` element (charts, SmartArt, embedded OLE objects, or a malformed/empty `a:tbl`) — a fidelity signal for `buildSourceImportMetadata`, the same way `imagesSkipped` already works. */
90
+ tablesDegraded?: number;
78
91
  }
79
92
  export interface ParsedPptxGrid {
80
93
  color: string;
@@ -81,6 +81,7 @@ export async function parsePptxPresentation(fileBuffer) {
81
81
  const metadata = parsePptxSlideMetadata(slide);
82
82
  let elements = [];
83
83
  const images = [];
84
+ const tablesDegraded = { count: 0 };
84
85
  const relationshipPath = slidePath.replace(/slides\/(slide\d+\.xml)/, "slides/_rels/$1.rels");
85
86
  const slideRelationshipsXml = await zip
86
87
  .file(relationshipPath)
@@ -109,6 +110,7 @@ export async function parsePptxPresentation(fileBuffer) {
109
110
  slideWidthEmu,
110
111
  slideHeightEmu,
111
112
  images,
113
+ tablesDegraded,
112
114
  colorContext: slideMasterContext.colorContext,
113
115
  placeholderDefaults: slideMasterContext.placeholderDefaults,
114
116
  });
@@ -138,6 +140,9 @@ export async function parsePptxPresentation(fileBuffer) {
138
140
  ...(backgroundGrid ? { backgroundGrid } : {}),
139
141
  notes,
140
142
  layoutHint: guessLayoutHint(texts, images.length > 0),
143
+ ...(tablesDegraded.count > 0
144
+ ? { tablesDegraded: tablesDegraded.count }
145
+ : {}),
141
146
  ...metadata,
142
147
  });
143
148
  }
@@ -340,6 +345,34 @@ function levelFillsFromTextStyle(style) {
340
345
  }
341
346
  return fills;
342
347
  }
348
+ const IDENTITY_MAT = { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
349
+ /** Composes two transforms so `inner` is applied to a point first, then `outer`. */
350
+ function composeMat(outer, inner) {
351
+ return {
352
+ a: outer.a * inner.a + outer.c * inner.b,
353
+ b: outer.b * inner.a + outer.d * inner.b,
354
+ c: outer.a * inner.c + outer.c * inner.d,
355
+ d: outer.b * inner.c + outer.d * inner.d,
356
+ e: outer.a * inner.e + outer.c * inner.f + outer.e,
357
+ f: outer.b * inner.e + outer.d * inner.f + outer.f,
358
+ };
359
+ }
360
+ function scaleMat(sx, sy) {
361
+ return { a: sx, b: 0, c: 0, d: sy, e: 0, f: 0 };
362
+ }
363
+ function translateMat(tx, ty) {
364
+ return { a: 1, b: 0, c: 0, d: 1, e: tx, f: ty };
365
+ }
366
+ function rotateAroundMat(degrees, cx, cy) {
367
+ const radians = (degrees * Math.PI) / 180;
368
+ const cos = Math.cos(radians);
369
+ const sin = Math.sin(radians);
370
+ const rotation = { a: cos, b: sin, c: -sin, d: cos, e: 0, f: 0 };
371
+ return composeMat(translateMat(cx, cy), composeMat(rotation, translateMat(-cx, -cy)));
372
+ }
373
+ function applyMatPoint(m, x, y) {
374
+ return { x: m.a * x + m.c * y + m.e, y: m.b * x + m.d * y + m.f };
375
+ }
343
376
  const SHAPE_ELEMENT_NAMES = new Set([
344
377
  "sp",
345
378
  "pic",
@@ -347,14 +380,14 @@ const SHAPE_ELEMENT_NAMES = new Set([
347
380
  "cxnSp",
348
381
  "graphicFrame",
349
382
  ]);
383
+ /** Standard 16:9 widescreen slide size — a last-resort content box for a placeholder shape whose own `<a:xfrm>` is missing and the deck's own slide size wasn't determinable either. */
384
+ const FALLBACK_SLIDE_WIDTH_EMU = 12192000;
385
+ const FALLBACK_SLIDE_HEIGHT_EMU = 6858000;
350
386
  async function parseSlideElements(args) {
351
387
  const fragments = extractDirectShapeFragments(args.xml, "spTree");
352
388
  const elements = [];
353
389
  const context = {
354
- originX: 0,
355
- originY: 0,
356
- scaleX: 1,
357
- scaleY: 1,
390
+ matrix: IDENTITY_MAT,
358
391
  rotation: 0,
359
392
  };
360
393
  for (const fragment of fragments) {
@@ -406,13 +439,20 @@ async function parseShapeFragment(fragment, args) {
406
439
  const childExtent = readPoint(groupXfrm?.["a:chExt"]);
407
440
  const groupScaleX = childExtent.x > 0 ? groupTransform.width / childExtent.x : 1;
408
441
  const groupScaleY = childExtent.y > 0 ? groupTransform.height / childExtent.y : 1;
442
+ // Places this group's children into the parent's coordinate space,
443
+ // ignoring the group's own rotation for now.
444
+ let localToParent = composeMat(translateMat(groupTransform.x - childOffset.x * groupScaleX, groupTransform.y - childOffset.y * groupScaleY), scaleMat(groupScaleX, groupScaleY));
445
+ // PowerPoint rotates the entire *placed* group box — every child already
446
+ // positioned by `localToParent` above — as a rigid body around that
447
+ // box's own center, not each child around its own center. Composing this
448
+ // rotation on top of the placement (instead of only summing `rotation`
449
+ // degrees, as before) is what makes children actually orbit the group's
450
+ // pivot instead of just spinning in place.
451
+ if (groupTransform.rotation) {
452
+ localToParent = composeMat(rotateAroundMat(groupTransform.rotation, groupTransform.x + groupTransform.width / 2, groupTransform.y + groupTransform.height / 2), localToParent);
453
+ }
409
454
  const nextContext = {
410
- originX: args.context.originX +
411
- args.context.scaleX * (groupTransform.x - childOffset.x * groupScaleX),
412
- originY: args.context.originY +
413
- args.context.scaleY * (groupTransform.y - childOffset.y * groupScaleY),
414
- scaleX: args.context.scaleX * groupScaleX,
415
- scaleY: args.context.scaleY * groupScaleY,
455
+ matrix: composeMat(args.context.matrix, localToParent),
416
456
  rotation: args.context.rotation + (groupTransform.rotation ?? 0),
417
457
  };
418
458
  const output = [];
@@ -424,7 +464,29 @@ async function parseShapeFragment(fragment, args) {
424
464
  }
425
465
  return output;
426
466
  }
427
- const transform = applyTransform(readTransform(node, "p:spPr"), args.context);
467
+ if (entry === "graphicFrame") {
468
+ return parseGraphicFrameFragment(node, args);
469
+ }
470
+ const localTransform = readTransform(node, "p:spPr");
471
+ // A placeholder shape (`<p:ph>`) commonly omits its own `<a:xfrm>` on the
472
+ // slide, inheriting position/size from the matching placeholder on the
473
+ // slide layout/master instead — that layout/master inheritance chain isn't
474
+ // implemented, so without this fallback the shape silently lands at a
475
+ // literal 0×0 box, making real title/body text invisible instead of just
476
+ // mispositioned. This substitutes the slide's own content box so the text
477
+ // is at least visible; it won't match the placeholder's real authored
478
+ // position/size.
479
+ const hasOwnSize = localTransform.width > 0 && localTransform.height > 0;
480
+ const effectiveLocalTransform = entry === "sp" && !hasOwnSize
481
+ ? {
482
+ ...localTransform,
483
+ x: 0,
484
+ y: 0,
485
+ width: args.slideWidthEmu ?? FALLBACK_SLIDE_WIDTH_EMU,
486
+ height: args.slideHeightEmu ?? FALLBACK_SLIDE_HEIGHT_EMU,
487
+ }
488
+ : localTransform;
489
+ const transform = applyTransform(effectiveLocalTransform, args.context);
428
490
  const id = readShapeId(node);
429
491
  const name = readShapeName(node);
430
492
  const placeholderType = stringValue(record(record(record(node["p:nvSpPr"])?.["p:nvPr"])?.["p:ph"])?.["@_type"]);
@@ -502,6 +564,64 @@ async function parseShapeFragment(fragment, args) {
502
564
  }
503
565
  return [];
504
566
  }
567
+ /**
568
+ * `p:graphicFrame` has no `p:spPr` (its transform lives at `p:xfrm` directly)
569
+ * and its content is `a:graphic/a:graphicData` rather than `p:txBody` — most
570
+ * commonly a table, but also charts, SmartArt, and embedded OLE objects that
571
+ * have no shape structure we can reconstruct. Only tables convert; anything
572
+ * else is counted as a dropped-content fidelity signal instead of silently
573
+ * vanishing with no signal at all.
574
+ */
575
+ function parseGraphicFrameFragment(node, args) {
576
+ const transform = applyTransform(transformFromXfrmNode(record(node["p:xfrm"])), args.context);
577
+ const table = parseGraphicFrameTable(node, args.colorContext);
578
+ if (!table) {
579
+ args.tablesDegraded.count += 1;
580
+ return [];
581
+ }
582
+ return [
583
+ {
584
+ id: readShapeId(node),
585
+ name: readShapeName(node),
586
+ kind: "table",
587
+ ...transform,
588
+ table,
589
+ },
590
+ ];
591
+ }
592
+ /** Reads a graphicFrame's `a:graphic/a:graphicData/a:tbl` into a row/cell grid. Returns `undefined` for a non-table graphicFrame (chart/SmartArt/OLE) or a table with no rows. */
593
+ function parseGraphicFrameTable(node, context) {
594
+ const graphicData = record(record(node["a:graphic"])?.["a:graphicData"]);
595
+ const tbl = record(graphicData?.["a:tbl"]);
596
+ if (!tbl)
597
+ return undefined;
598
+ const rows = asArray(tbl["a:tr"]).map((rawRow) => {
599
+ const row = record(rawRow);
600
+ const cells = [];
601
+ for (const rawCell of asArray(row?.["a:tc"])) {
602
+ const cell = record(rawCell);
603
+ if (!cell)
604
+ continue;
605
+ // A merge-continuation cell's content is already represented once, by
606
+ // the spanning cell's gridSpan/rowSpan below.
607
+ if (cell["@_hMerge"] === "1" || cell["@_vMerge"] === "1")
608
+ continue;
609
+ const gridSpan = Number(cell["@_gridSpan"]);
610
+ const rowSpan = Number(cell["@_rowSpan"]);
611
+ const fill = parseShapeFill(record(cell["a:tcPr"]), context);
612
+ cells.push({
613
+ paragraphs: parseTextBodyParagraphs(record(cell["a:txBody"]), context),
614
+ ...(Number.isFinite(gridSpan) && gridSpan > 1
615
+ ? { colSpan: gridSpan }
616
+ : {}),
617
+ ...(Number.isFinite(rowSpan) && rowSpan > 1 ? { rowSpan } : {}),
618
+ ...(fill ? { fill } : {}),
619
+ });
620
+ }
621
+ return cells;
622
+ });
623
+ return rows.length > 0 ? { rows } : undefined;
624
+ }
505
625
  function extractDirectShapeFragments(xml, container) {
506
626
  const containerMatch = new RegExp(`<p:${container}\\b[^>]*>`, "i").exec(xml);
507
627
  if (!containerMatch)
@@ -561,12 +681,15 @@ function findMatchingXmlTag(xml, start, localName) {
561
681
  }
562
682
  function readShapeId(node) {
563
683
  const cNvPr = record(record(node["p:nvSpPr"])?.["p:cNvPr"]) ??
564
- record(record(node["p:nvPicPr"])?.["p:cNvPr"]);
684
+ record(record(node["p:nvPicPr"])?.["p:cNvPr"]) ??
685
+ record(record(node["p:nvGraphicFramePr"])?.["p:cNvPr"]);
565
686
  return (stringValue(cNvPr?.["@_id"]) ??
566
687
  `shape-${Math.random().toString(36).slice(2)}`);
567
688
  }
568
689
  function readShapeName(node) {
569
- return stringValue(record(record(node["p:nvSpPr"])?.["p:cNvPr"])?.["@_name"]);
690
+ const cNvPr = record(record(node["p:nvSpPr"])?.["p:cNvPr"]) ??
691
+ record(record(node["p:nvGraphicFramePr"])?.["p:cNvPr"]);
692
+ return stringValue(cNvPr?.["@_name"]);
570
693
  }
571
694
  function readPoint(value) {
572
695
  const point = record(value);
@@ -583,7 +706,10 @@ function readExtent(value) {
583
706
  };
584
707
  }
585
708
  function readTransform(node, key) {
586
- const xfrm = record(record(node[key])?.["a:xfrm"]);
709
+ return transformFromXfrmNode(record(record(node[key])?.["a:xfrm"]));
710
+ }
711
+ /** Shared by `readTransform` (a wrapper-nested `a:xfrm`, e.g. `p:spPr/a:xfrm`) and `graphicFrame`, whose `p:xfrm` sits directly on the node instead of inside a wrapper element. */
712
+ function transformFromXfrmNode(xfrm) {
587
713
  const off = readPoint(xfrm?.["a:off"]);
588
714
  const ext = readExtent(xfrm?.["a:ext"]);
589
715
  const rawRotation = Number(xfrm?.["@_rot"]);
@@ -598,20 +724,40 @@ function readTransform(node, key) {
598
724
  };
599
725
  }
600
726
  function applyTransform(transform, context) {
727
+ const scaleX = Math.hypot(context.matrix.a, context.matrix.b);
728
+ const scaleY = Math.hypot(context.matrix.c, context.matrix.d);
729
+ const width = transform.width * scaleX;
730
+ const height = transform.height * scaleY;
731
+ // Map the shape's own (un-rotated) box center through the accumulated
732
+ // group matrix so a child inside a rotated group orbits the group's pivot
733
+ // — the box's own visual spin is applied separately below via `rotation`,
734
+ // so only the center (not the whole box) needs to go through the matrix.
735
+ const center = applyMatPoint(context.matrix, transform.x + transform.width / 2, transform.y + transform.height / 2);
736
+ const rotation = (transform.rotation ?? 0) + context.rotation;
601
737
  return {
602
- x: context.originX + transform.x * context.scaleX,
603
- y: context.originY + transform.y * context.scaleY,
604
- width: transform.width * context.scaleX,
605
- height: transform.height * context.scaleY,
606
- ...(transform.rotation || context.rotation
607
- ? { rotation: (transform.rotation ?? 0) + context.rotation }
608
- : {}),
738
+ x: center.x - width / 2,
739
+ y: center.y - height / 2,
740
+ width,
741
+ height,
742
+ ...(rotation ? { rotation } : {}),
609
743
  };
610
744
  }
611
745
  function parseTextBody(node, context) {
612
- const txBody = record(node["p:txBody"]);
746
+ return parseTextBodyParagraphs(record(node["p:txBody"]), context);
747
+ }
748
+ /** PowerPoint's `buAutoNum` variants (arabicPeriod, alphaLcPeriod, romanUcPeriod, ...) all number the same underlying sequence — approximating every variant with arabic digits keeps list order/grouping correct even though the glyph style doesn't match `type` exactly. */
749
+ function formatAutoNumBullet(n) {
750
+ return `${n}.`;
751
+ }
752
+ /** Core `a:p` paragraph parsing, shared by a shape's `p:txBody` and a table cell's `a:txBody` (which sit at different paths in their parent node, so the caller resolves the `txBody` record itself). */
753
+ function parseTextBodyParagraphs(txBody, context) {
613
754
  if (!txBody)
614
755
  return [];
756
+ // `a:buAutoNum` carries no explicit number — PowerPoint derives it from
757
+ // paragraph order — so the sequence has to be tracked per nesting level as
758
+ // paragraphs are walked in document order, restarting whenever a deeper
759
+ // level's own sequence begins or a non-auto-numbered paragraph interrupts it.
760
+ const autoNumCounters = new Map();
615
761
  return asArray(txBody["a:p"]).map((rawParagraph) => {
616
762
  const paragraph = record(rawParagraph);
617
763
  const pPr = record(paragraph?.["a:pPr"]);
@@ -644,12 +790,30 @@ function parseTextBody(node, context) {
644
790
  const spaceBeforePt = parsePoints(pPr?.["a:spcBef"]);
645
791
  const spaceAfterPt = parsePoints(pPr?.["a:spcAft"]);
646
792
  const alignment = mapAlignment(stringValue(pPr?.["@_algn"]));
793
+ const level = Number.isFinite(Number(pPr?.["@_lvl"]))
794
+ ? Number(pPr?.["@_lvl"])
795
+ : 0;
796
+ let autoNumBullet;
797
+ if (pPr?.["a:buAutoNum"] !== undefined && pPr?.["a:buNone"] === undefined) {
798
+ for (const key of [...autoNumCounters.keys()]) {
799
+ if (key > level)
800
+ autoNumCounters.delete(key);
801
+ }
802
+ const next = (autoNumCounters.get(level) ?? 0) + 1;
803
+ autoNumCounters.set(level, next);
804
+ autoNumBullet = formatAutoNumBullet(next);
805
+ }
806
+ else if (bullet?.["@_char"] === undefined) {
807
+ autoNumCounters.clear();
808
+ }
647
809
  return {
648
810
  runs,
649
811
  ...(alignment ? { alignment } : {}),
650
812
  ...(bullet?.["@_char"] && !pPr?.["a:buNone"]
651
813
  ? { bulletChar: String(bullet["@_char"]) }
652
- : {}),
814
+ : autoNumBullet
815
+ ? { bulletChar: autoNumBullet }
816
+ : {}),
653
817
  ...(bulletColor ? { bulletColor } : {}),
654
818
  ...(bulletFont ? { bulletFontFamily: bulletFont } : {}),
655
819
  ...(Number.isFinite(bulletSize) && bulletSize > 0
@@ -694,7 +858,14 @@ function parseShapeFill(shapeProperties, context) {
694
858
  return undefined;
695
859
  if (shapeProperties["a:noFill"] !== undefined)
696
860
  return undefined;
697
- return parseColor(record(shapeProperties["a:solidFill"]), context);
861
+ const solid = parseColor(record(shapeProperties["a:solidFill"]), context);
862
+ if (solid)
863
+ return solid;
864
+ // A gradient fill (`<a:gradFill>`) has no `a:solidFill` at all — approximate
865
+ // it with its first stop's resolved color rather than leaving the shape
866
+ // unfilled, which silently turned every gradient-filled shape transparent.
867
+ const stops = asArray(record(record(shapeProperties["a:gradFill"])?.["a:gsLst"])?.["a:gs"]);
868
+ return parseColor(record(stops[0]), context);
698
869
  }
699
870
  function parseShapeLine(shapeProperties, context) {
700
871
  const line = record(shapeProperties?.["a:ln"]);
@@ -819,32 +990,42 @@ function readColorTransforms(node) {
819
990
  const raw = stringValue(record(node[key])?.["@_val"]);
820
991
  return raw !== undefined ? Number(raw) / 100000 : undefined;
821
992
  };
993
+ const alphaRaw = stringValue(record(node["a:alpha"])?.["@_val"]);
822
994
  return {
823
995
  lumMod: percent("a:lumMod"),
824
996
  lumOff: percent("a:lumOff"),
825
997
  tint: percent("a:tint"),
826
998
  shade: percent("a:shade"),
999
+ ...(alphaRaw !== undefined
1000
+ ? { alphaPercent: Number(alphaRaw) / 1000 }
1001
+ : {}),
827
1002
  };
828
1003
  }
1004
+ /** Colors flow through the parser as plain `#rrggbb` hex strings, and every consumer just drops that string straight into CSS — an 8-digit `#rrggbbaa` hex is valid CSS and needs no consumer changes, so alpha rides along as extra hex digits instead of a new color shape. */
829
1005
  function applyColorTransforms(hex, transforms) {
830
- const { lumMod, lumOff, tint, shade } = transforms;
831
- if (lumMod === undefined &&
832
- lumOff === undefined &&
833
- tint === undefined &&
834
- shade === undefined) {
835
- return hex;
1006
+ const { lumMod, lumOff, tint, shade, alphaPercent } = transforms;
1007
+ let result = hex;
1008
+ if (lumMod !== undefined ||
1009
+ lumOff !== undefined ||
1010
+ tint !== undefined ||
1011
+ shade !== undefined) {
1012
+ const [h, s, initialL] = hexToHsl(hex);
1013
+ let l = initialL;
1014
+ if (lumMod !== undefined)
1015
+ l *= lumMod;
1016
+ if (lumOff !== undefined)
1017
+ l += lumOff;
1018
+ if (tint !== undefined)
1019
+ l = l * tint + (1 - tint);
1020
+ if (shade !== undefined)
1021
+ l *= shade;
1022
+ result = hslToHex(h, s, Math.min(1, Math.max(0, l)));
1023
+ }
1024
+ if (alphaPercent !== undefined) {
1025
+ const alphaByte = Math.max(0, Math.min(255, Math.round((alphaPercent / 100) * 255)));
1026
+ result = `${result}${alphaByte.toString(16).padStart(2, "0")}`;
836
1027
  }
837
- const [h, s, initialL] = hexToHsl(hex);
838
- let l = initialL;
839
- if (lumMod !== undefined)
840
- l *= lumMod;
841
- if (lumOff !== undefined)
842
- l += lumOff;
843
- if (tint !== undefined)
844
- l = l * tint + (1 - tint);
845
- if (shade !== undefined)
846
- l *= shade;
847
- return hslToHex(h, s, Math.min(1, Math.max(0, l)));
1028
+ return result;
848
1029
  }
849
1030
  function parseColor(value, context) {
850
1031
  if (!value)
@@ -273,10 +273,14 @@ const messages = {
273
273
  "errorMessages.startNewChat": "بدء محادثة جديدة",
274
274
  "errorMessages.upgradeAtBuilder": "الترقية عبر Builder.io",
275
275
  "feedback.inaccurate": "غير دقيق",
276
+ "feedback.keyboardHint": "{{shortcut}} Enter للإرسال",
276
277
  "feedback.notHelpful": "غير مفيد",
278
+ "feedback.placeholder": "أخبرنا بما حدث من خطأ...",
279
+ "feedback.submit": "إرسال",
277
280
  "feedback.thumbsDown": "عدم إعجاب",
278
281
  "feedback.thumbsUp": "إعجاب",
279
282
  "feedback.tooSlow": "بطيء جدًا",
283
+ "feedback.whatWentWrong": "ما الذي حدث بشكل خاطئ؟",
280
284
  "feedback.wrongTool": "أداة غير صحيحة",
281
285
  "header.switchToCli": "التبديل إلى CLI",
282
286
  "history.active": "نشطة",
@@ -401,10 +401,14 @@ const messages = {
401
401
  "errorMessages.startNewChat": "Neuen Chat starten",
402
402
  "errorMessages.upgradeAtBuilder": "Bei Builder.io upgraden",
403
403
  "feedback.inaccurate": "Ungenau",
404
+ "feedback.keyboardHint": "{{shortcut}} Enter zum Senden",
404
405
  "feedback.notHelpful": "Nicht hilfreich",
406
+ "feedback.placeholder": "Sag uns, was schiefgelaufen ist...",
407
+ "feedback.submit": "Senden",
405
408
  "feedback.thumbsDown": "Daumen runter",
406
409
  "feedback.thumbsUp": "Daumen hoch",
407
410
  "feedback.tooSlow": "Zu langsam",
411
+ "feedback.whatWentWrong": "Was ist schiefgelaufen?",
408
412
  "feedback.wrongTool": "Falsches Tool",
409
413
  "contextMeter.ariaLabel": "Kontext {{percent}} %, {{totalTokens}}{{breakdown}}. Kontextanalyse öffnen.",
410
414
  "contextMeter.breakdown": " gesamt: {{systemTokens}} System + {{conversationTokens}} Unterhaltung",
@@ -265,10 +265,14 @@ declare const messages: {
265
265
  readonly "errorMessages.startNewChat": "Start new chat";
266
266
  readonly "errorMessages.upgradeAtBuilder": "Upgrade at Builder.io";
267
267
  readonly "feedback.inaccurate": "Inaccurate";
268
+ readonly "feedback.keyboardHint": "{{shortcut}} Enter to send";
268
269
  readonly "feedback.notHelpful": "Not helpful";
270
+ readonly "feedback.placeholder": "Tell us what went wrong...";
271
+ readonly "feedback.submit": "Submit";
269
272
  readonly "feedback.thumbsDown": "Thumbs down";
270
273
  readonly "feedback.thumbsUp": "Thumbs up";
271
274
  readonly "feedback.tooSlow": "Too slow";
275
+ readonly "feedback.whatWentWrong": "What went wrong?";
272
276
  readonly "feedback.wrongTool": "Wrong tool";
273
277
  readonly "header.switchToCli": "Switch to CLI";
274
278
  readonly "history.active": "Active";
@@ -265,10 +265,14 @@ const messages = {
265
265
  "errorMessages.startNewChat": "Start new chat",
266
266
  "errorMessages.upgradeAtBuilder": "Upgrade at Builder.io",
267
267
  "feedback.inaccurate": "Inaccurate",
268
+ "feedback.keyboardHint": "{{shortcut}} Enter to send",
268
269
  "feedback.notHelpful": "Not helpful",
270
+ "feedback.placeholder": "Tell us what went wrong...",
271
+ "feedback.submit": "Submit",
269
272
  "feedback.thumbsDown": "Thumbs down",
270
273
  "feedback.thumbsUp": "Thumbs up",
271
274
  "feedback.tooSlow": "Too slow",
275
+ "feedback.whatWentWrong": "What went wrong?",
272
276
  "feedback.wrongTool": "Wrong tool",
273
277
  "header.switchToCli": "Switch to CLI",
274
278
  "history.active": "Active",
@@ -408,10 +408,14 @@ const messages = {
408
408
  "errorMessages.startNewChat": "Iniciar un chat nuevo",
409
409
  "errorMessages.upgradeAtBuilder": "Mejorar el plan en Builder.io",
410
410
  "feedback.inaccurate": "Impreciso",
411
+ "feedback.keyboardHint": "{{shortcut}} Enter para enviar",
411
412
  "feedback.notHelpful": "Poco útil",
413
+ "feedback.placeholder": "Cuéntanos qué salió mal...",
414
+ "feedback.submit": "Enviar",
412
415
  "feedback.thumbsDown": "No me gusta",
413
416
  "feedback.thumbsUp": "Me gusta",
414
417
  "feedback.tooSlow": "Demasiado lento",
418
+ "feedback.whatWentWrong": "¿Qué salió mal?",
415
419
  "feedback.wrongTool": "Herramienta incorrecta",
416
420
  "contextMeter.ariaLabel": "Contexto {{percent}} %, {{totalTokens}}{{breakdown}}. Abrir el análisis de contexto.",
417
421
  "contextMeter.breakdown": " en total: {{systemTokens}} del sistema + {{conversationTokens}} de la conversación",
@@ -408,10 +408,14 @@ const messages = {
408
408
  "errorMessages.startNewChat": "Démarrer une nouvelle discussion",
409
409
  "errorMessages.upgradeAtBuilder": "Mettre à niveau sur Builder.io",
410
410
  "feedback.inaccurate": "Inexact",
411
+ "feedback.keyboardHint": "{{shortcut}} Entrée pour envoyer",
411
412
  "feedback.notHelpful": "Peu utile",
413
+ "feedback.placeholder": "Dites-nous ce qui n'a pas fonctionné...",
414
+ "feedback.submit": "Envoyer",
412
415
  "feedback.thumbsDown": "Pouce vers le bas",
413
416
  "feedback.thumbsUp": "Pouce vers le haut",
414
417
  "feedback.tooSlow": "Trop lent",
418
+ "feedback.whatWentWrong": "Qu'est-ce qui n'a pas fonctionné ?",
415
419
  "feedback.wrongTool": "Mauvais outil",
416
420
  "contextMeter.ariaLabel": "Contexte {{percent}} %, {{totalTokens}}{{breakdown}}. Ouvrir l’analyse du contexte.",
417
421
  "contextMeter.breakdown": " au total : {{systemTokens}} système + {{conversationTokens}} conversation",
@@ -265,10 +265,14 @@ const messages = {
265
265
  "errorMessages.startNewChat": "नई चैट शुरू करें",
266
266
  "errorMessages.upgradeAtBuilder": "Builder.io पर अपग्रेड करें",
267
267
  "feedback.inaccurate": "गलत जानकारी",
268
+ "feedback.keyboardHint": "भेजने के लिए {{shortcut}} Enter",
268
269
  "feedback.notHelpful": "उपयोगी नहीं",
270
+ "feedback.placeholder": "बताएं कि क्या गलत हुआ...",
271
+ "feedback.submit": "जमा करें",
269
272
  "feedback.thumbsDown": "नापसंद",
270
273
  "feedback.thumbsUp": "पसंद",
271
274
  "feedback.tooSlow": "बहुत धीमा",
275
+ "feedback.whatWentWrong": "क्या गलत हुआ?",
272
276
  "feedback.wrongTool": "गलत टूल",
273
277
  "header.switchToCli": "CLI पर जाएँ",
274
278
  "history.active": "सक्रिय",
@@ -263,10 +263,14 @@ const messages = {
263
263
  "errorMessages.startNewChat": "新しいチャットを開始",
264
264
  "errorMessages.upgradeAtBuilder": "Builder.io でアップグレード",
265
265
  "feedback.inaccurate": "不正確",
266
+ "feedback.keyboardHint": "{{shortcut}} Enter で送信",
266
267
  "feedback.notHelpful": "役に立たない",
268
+ "feedback.placeholder": "問題点を教えてください…",
269
+ "feedback.submit": "送信",
267
270
  "feedback.thumbsDown": "低評価",
268
271
  "feedback.thumbsUp": "高評価",
269
272
  "feedback.tooSlow": "遅すぎる",
273
+ "feedback.whatWentWrong": "何が問題でしたか?",
270
274
  "feedback.wrongTool": "ツールが不適切",
271
275
  "header.switchToCli": "CLI に切り替え",
272
276
  "history.active": "アクティブ",
@@ -263,10 +263,14 @@ const messages = {
263
263
  "errorMessages.startNewChat": "새 채팅 시작",
264
264
  "errorMessages.upgradeAtBuilder": "Builder.io에서 업그레이드",
265
265
  "feedback.inaccurate": "부정확함",
266
+ "feedback.keyboardHint": "보내려면 {{shortcut}} Enter",
266
267
  "feedback.notHelpful": "도움이 되지 않음",
268
+ "feedback.placeholder": "무엇이 문제였는지 알려주세요…",
269
+ "feedback.submit": "제출",
267
270
  "feedback.thumbsDown": "싫어요",
268
271
  "feedback.thumbsUp": "좋아요",
269
272
  "feedback.tooSlow": "너무 느림",
273
+ "feedback.whatWentWrong": "무엇이 잘못되었나요?",
270
274
  "feedback.wrongTool": "잘못된 도구",
271
275
  "header.switchToCli": "CLI로 전환",
272
276
  "history.active": "활성",
@@ -408,10 +408,14 @@ const messages = {
408
408
  "errorMessages.startNewChat": "Iniciar novo chat",
409
409
  "errorMessages.upgradeAtBuilder": "Fazer upgrade no Builder.io",
410
410
  "feedback.inaccurate": "Impreciso",
411
+ "feedback.keyboardHint": "{{shortcut}} Enter para enviar",
411
412
  "feedback.notHelpful": "Pouco útil",
413
+ "feedback.placeholder": "Conte-nos o que deu errado...",
414
+ "feedback.submit": "Enviar",
412
415
  "feedback.thumbsDown": "Não gostei",
413
416
  "feedback.thumbsUp": "Gostei",
414
417
  "feedback.tooSlow": "Muito lento",
418
+ "feedback.whatWentWrong": "O que deu errado?",
415
419
  "feedback.wrongTool": "Ferramenta errada",
416
420
  "contextMeter.ariaLabel": "Contexto {{percent}}%, {{totalTokens}}{{breakdown}}. Abrir a análise de contexto.",
417
421
  "contextMeter.breakdown": " no total: {{systemTokens}} do sistema + {{conversationTokens}} da conversa",
@@ -263,10 +263,14 @@ const messages = {
263
263
  "errorMessages.startNewChat": "开始新聊天",
264
264
  "errorMessages.upgradeAtBuilder": "在 Builder.io 升级",
265
265
  "feedback.inaccurate": "不准确",
266
+ "feedback.keyboardHint": "{{shortcut}} Enter 发送",
266
267
  "feedback.notHelpful": "没有帮助",
268
+ "feedback.placeholder": "告诉我们哪里出了问题……",
269
+ "feedback.submit": "提交",
267
270
  "feedback.thumbsDown": "不喜欢",
268
271
  "feedback.thumbsUp": "喜欢",
269
272
  "feedback.tooSlow": "太慢",
273
+ "feedback.whatWentWrong": "哪里出了问题?",
270
274
  "feedback.wrongTool": "工具错误",
271
275
  "header.switchToCli": "切换到 CLI",
272
276
  "history.active": "活跃",
@@ -263,10 +263,14 @@ const messages = {
263
263
  "errorMessages.startNewChat": "開始新聊天",
264
264
  "errorMessages.upgradeAtBuilder": "在 Builder.io 升級",
265
265
  "feedback.inaccurate": "不準確",
266
+ "feedback.keyboardHint": "{{shortcut}} Enter 送出",
266
267
  "feedback.notHelpful": "沒有幫助",
268
+ "feedback.placeholder": "告訴我們哪裡出了問題……",
269
+ "feedback.submit": "送出",
267
270
  "feedback.thumbsDown": "不喜歡",
268
271
  "feedback.thumbsUp": "喜歡",
269
272
  "feedback.tooSlow": "太慢",
273
+ "feedback.whatWentWrong": "哪裡出了問題?",
270
274
  "feedback.wrongTool": "工具錯誤",
271
275
  "header.switchToCli": "切換到 CLI",
272
276
  "history.active": "使用中",
@@ -267,10 +267,14 @@ export declare const englishAgentChatMessages: {
267
267
  readonly "errorMessages.startNewChat": "Start new chat";
268
268
  readonly "errorMessages.upgradeAtBuilder": "Upgrade at Builder.io";
269
269
  readonly "feedback.inaccurate": "Inaccurate";
270
+ readonly "feedback.keyboardHint": "{{shortcut}} Enter to send";
270
271
  readonly "feedback.notHelpful": "Not helpful";
272
+ readonly "feedback.placeholder": "Tell us what went wrong...";
273
+ readonly "feedback.submit": "Submit";
271
274
  readonly "feedback.thumbsDown": "Thumbs down";
272
275
  readonly "feedback.thumbsUp": "Thumbs up";
273
276
  readonly "feedback.tooSlow": "Too slow";
277
+ readonly "feedback.whatWentWrong": "What went wrong?";
274
278
  readonly "feedback.wrongTool": "Wrong tool";
275
279
  readonly "header.switchToCli": "Switch to CLI";
276
280
  readonly "history.active": "Active";