agent-canvas 0.1.36 → 0.1.38

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.
@@ -14,7 +14,6 @@ jobs:
14
14
  uses: ./.github/workflows/deploy-worker.yml
15
15
  with:
16
16
  env: prod
17
- secrets:
18
- CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
19
- CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
20
- SHARE_AUTH_TOKEN: ${{ secrets.SHARE_AUTH_TOKEN_PROD }}
17
+ # inherit so the reusable workflow can resolve secrets; the env-scoped
18
+ # values (set on the `prod` environment) win over repo-level ones.
19
+ secrets: inherit
@@ -16,7 +16,6 @@ jobs:
16
16
  uses: ./.github/workflows/deploy-worker.yml
17
17
  with:
18
18
  env: stage
19
- secrets:
20
- CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
21
- CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
22
- SHARE_AUTH_TOKEN: ${{ secrets.SHARE_AUTH_TOKEN_STAGE }}
19
+ # inherit so the reusable workflow can resolve secrets; the env-scoped
20
+ # values (set on the `stage` environment) win over repo-level ones.
21
+ secrets: inherit
@@ -7,14 +7,13 @@ on:
7
7
  description: "Target environment (stage or prod)"
8
8
  required: true
9
9
  type: string
10
- secrets:
11
- CLOUDFLARE_API_TOKEN:
12
- required: true
13
- CLOUDFLARE_ACCOUNT_ID:
14
- required: true
15
- SHARE_AUTH_TOKEN:
16
- description: "Bearer token the daemon must send to POST /shares. Optional but strongly recommended in production."
17
- required: false
10
+ # No `secrets:` inputs on purpose. The deploy job below declares
11
+ # `environment: ${{ inputs.env }}`, so referencing `secrets.X` directly
12
+ # resolves the matching *environment* secret (falling back to a
13
+ # repo-level secret of the same name). Callers pass `secrets: inherit`
14
+ # so org/repo secrets remain reachable too. This is what makes the
15
+ # CLOUDFLARE_* / SHARE_AUTH_TOKEN values environment-scoped — a prod
16
+ # token can never be read by a stage deploy and vice versa.
18
17
 
19
18
  jobs:
20
19
  deploy:
@@ -59,7 +59,11 @@ export async function handlePush(args: string[]) {
59
59
  // Skip restart only for clear user errors (syntax errors with line/col,
60
60
  // runtime validation errors) — those won't be fixed by a restart.
61
61
  const isUserError = !result.ok && result.errors && Object.values(result.errors).every((e: any) =>
62
- typeof e === "string" && (/\(line \d+, col \d+\)/.test(e) || e.startsWith("Runtime error:")));
62
+ typeof e === "string" && (
63
+ /\(line \d+, col \d+\)/.test(e) ||
64
+ /Syntax Error|Parse error|Mermaid syntax error/i.test(e) ||
65
+ e.startsWith("Runtime error:")
66
+ ));
63
67
  const shouldRestart = !result.ok && result.error === "Canvas compilation failed" && !isUserError;
64
68
  if (shouldRestart) {
65
69
  console.error("Compilation failed, restarting daemon and retrying...");
@@ -3,6 +3,27 @@ import { marked } from "marked";
3
3
 
4
4
  marked.setOptions({ gfm: true, breaks: false });
5
5
 
6
+ /** Host tags that flow inline and must not break a prose run into separate blocks. */
7
+ const INLINE_TAGS = new Set([
8
+ "a", "abbr", "b", "br", "code", "del", "em", "i", "kbd", "mark",
9
+ "q", "s", "small", "span", "strong", "sub", "sup", "time", "u",
10
+ ]);
11
+
12
+ /** Custom components that render inline (e.g. <Priority>). */
13
+ const INLINE_COMPONENTS = new Set(["Priority"]);
14
+
15
+ function isInlineNode(node: React.ReactNode): boolean {
16
+ if (typeof node === "string" || typeof node === "number") return true;
17
+ if (!React.isValidElement(node)) return false;
18
+ const type = node.type;
19
+ if (typeof type === "string") return INLINE_TAGS.has(type);
20
+ if (typeof type === "function") {
21
+ const name = (type as { displayName?: string; name?: string }).displayName ?? (type as { name?: string }).name;
22
+ return name ? INLINE_COMPONENTS.has(name) : false;
23
+ }
24
+ return false;
25
+ }
26
+
6
27
  function renderTextAsMarkdown(text: string, key: string): React.ReactNode {
7
28
  const trimmed = text.trim();
8
29
  if (!trimmed) return null;
@@ -18,30 +39,54 @@ function renderTextAsMarkdown(text: string, key: string): React.ReactNode {
18
39
  );
19
40
  }
20
41
 
42
+ /**
43
+ * Render a run of inline content (text + inline JSX like <code>/<strong>) as a single
44
+ * flowing paragraph. Text segments are parsed as inline markdown; JSX passes through.
45
+ * No `data-md` attribute so the markdown exporter treats it as an inline container.
46
+ */
47
+ function renderInlineRun(run: React.ReactNode[], key: string): React.ReactNode {
48
+ return (
49
+ <div key={key} className="prose-canvas">
50
+ {run.map((node, i) => {
51
+ if (typeof node === "string" || typeof node === "number") {
52
+ const html = marked.parseInline(String(node), { async: false }) as string;
53
+ return <span key={i} dangerouslySetInnerHTML={{ __html: html }} />;
54
+ }
55
+ return node;
56
+ })}
57
+ </div>
58
+ );
59
+ }
60
+
21
61
  /**
22
62
  * Auto-render markdown for string children, passing JSX children through.
23
- * Consecutive string runs are grouped into a single markdown block so multi-paragraph
24
- * prose between JSX elements renders correctly.
63
+ * Block-level JSX (Callout, Diff, etc.) breaks the prose into separate markdown blocks,
64
+ * while inline JSX (code, strong, links) stays within the surrounding paragraph so text
65
+ * keeps flowing instead of stacking one fragment per line.
25
66
  */
26
67
  export function renderMarkdownChildren(children: React.ReactNode): React.ReactNode {
27
68
  const kids = React.Children.toArray(children);
28
69
  if (kids.length === 0) return null;
29
70
 
30
71
  const result: React.ReactNode[] = [];
31
- let buffer = "";
32
- let bufferIdx = 0;
72
+ let segment: React.ReactNode[] = [];
73
+ let segIdx = 0;
33
74
 
34
75
  const flush = () => {
35
- if (buffer) {
36
- const node = renderTextAsMarkdown(buffer, `md-${bufferIdx++}`);
76
+ if (segment.length === 0) return;
77
+ const hasInlineJsx = segment.some((n) => typeof n !== "string" && typeof n !== "number");
78
+ if (hasInlineJsx) {
79
+ result.push(renderInlineRun(segment, `md-${segIdx++}`));
80
+ } else {
81
+ const node = renderTextAsMarkdown(segment.join(""), `md-${segIdx++}`);
37
82
  if (node) result.push(node);
38
- buffer = "";
39
83
  }
84
+ segment = [];
40
85
  };
41
86
 
42
87
  for (const child of kids) {
43
- if (typeof child === "string" || typeof child === "number") {
44
- buffer += String(child);
88
+ if (isInlineNode(child)) {
89
+ segment.push(child);
45
90
  } else {
46
91
  flush();
47
92
  result.push(child);