@agimon-ai/po-native-components 0.0.0-alpha.22 → 0.0.0-alpha.24

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 (57) hide show
  1. package/dist/components/AgentAvatar.d.mts +14 -0
  2. package/dist/components/AgentAvatar.d.mts.map +1 -0
  3. package/dist/components/AgentAvatar.mjs +87 -0
  4. package/dist/components/AgentAvatar.mjs.map +1 -0
  5. package/dist/components/Badge.d.mts +1 -1
  6. package/dist/components/Badge.mjs +1 -1
  7. package/dist/components/Button.mjs +1 -1
  8. package/dist/components/CodeBlock.d.mts.map +1 -1
  9. package/dist/components/CodeBlock.mjs +5 -34
  10. package/dist/components/CodeBlock.mjs.map +1 -1
  11. package/dist/components/CopyButton.d.mts +13 -0
  12. package/dist/components/CopyButton.d.mts.map +1 -0
  13. package/dist/components/CopyButton.mjs +43 -0
  14. package/dist/components/CopyButton.mjs.map +1 -0
  15. package/dist/components/Dialog.mjs +3 -3
  16. package/dist/components/Dot.d.mts +1 -1
  17. package/dist/components/Dot.mjs +2 -2
  18. package/dist/components/DropdownMenu.mjs +1 -1
  19. package/dist/components/HashlineLines.d.mts +13 -0
  20. package/dist/components/HashlineLines.d.mts.map +1 -0
  21. package/dist/components/HashlineLines.mjs +137 -0
  22. package/dist/components/HashlineLines.mjs.map +1 -0
  23. package/dist/components/Input.mjs +1 -1
  24. package/dist/components/Label.mjs +1 -1
  25. package/dist/components/List.mjs +1 -1
  26. package/dist/components/Markdown.d.mts +8 -1
  27. package/dist/components/Markdown.d.mts.map +1 -1
  28. package/dist/components/Markdown.mjs +199 -18
  29. package/dist/components/Markdown.mjs.map +1 -1
  30. package/dist/components/MessageItem.mjs +1 -1
  31. package/dist/components/SectionLabel.mjs +1 -1
  32. package/dist/components/Sheet.mjs +1 -1
  33. package/dist/components/SheetFrame.mjs +1 -1
  34. package/dist/components/Spinner.d.mts +1 -1
  35. package/dist/components/StatusBadge.d.mts +1 -1
  36. package/dist/components/SyntaxLine.d.mts +19 -0
  37. package/dist/components/SyntaxLine.d.mts.map +1 -0
  38. package/dist/components/SyntaxLine.mjs +132 -0
  39. package/dist/components/SyntaxLine.mjs.map +1 -0
  40. package/dist/components/Tabs.mjs +1 -1
  41. package/dist/components/ToolPathLink.d.mts +10 -0
  42. package/dist/components/ToolPathLink.d.mts.map +1 -0
  43. package/dist/components/ToolPathLink.mjs +40 -0
  44. package/dist/components/ToolPathLink.mjs.map +1 -0
  45. package/dist/icons/iconGlyphs.d.mts +16 -0
  46. package/dist/icons/iconGlyphs.d.mts.map +1 -1
  47. package/dist/icons/iconGlyphs.mjs +16 -0
  48. package/dist/icons/iconGlyphs.mjs.map +1 -1
  49. package/dist/icons/icons.d.mts +17 -1
  50. package/dist/icons/icons.d.mts.map +1 -1
  51. package/dist/icons/icons.mjs +5 -1
  52. package/dist/icons/icons.mjs.map +1 -1
  53. package/dist/index.d.mts +9 -4
  54. package/dist/index.mjs +9 -4
  55. package/dist/lib/markdown.mjs +101 -5
  56. package/dist/lib/markdown.mjs.map +1 -1
  57. package/package.json +4 -4
@@ -0,0 +1,14 @@
1
+ //#region src/components/AgentAvatar.d.ts
2
+ /** How an agent is doing, as the avatar's corner dot shows it. */
3
+ export type AgentState = 'running' | 'waiting' | 'done' | 'failed';
4
+ /** One letter for a first name, two for a hyphenated agent name (`po-developer` is PD). */
5
+ export declare function agentInitials(name: string): string;
6
+ /** An agent's face: its initials on a tint fixed by `seed`, with its state as a corner dot. */
7
+ export declare function AgentAvatar({ name, seed, state, size }: {
8
+ name: string;
9
+ seed?: string;
10
+ state?: AgentState;
11
+ size?: 'sm' | 'md';
12
+ }): import("react").JSX.Element;
13
+ //#endregion
14
+ //# sourceMappingURL=AgentAvatar.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentAvatar.d.mts","names":[],"sources":["../../src/components/AgentAvatar.tsx"],"mappings":";;YAQY;;wBAGI,cAAc;;wBA6Bd,cACd,MACA,MACA,OACA;EAEA;EACA;EACA,QAAQ;EACR;oBACD,IAAA"}
@@ -0,0 +1,87 @@
1
+ import { fontOf } from "../lib/fonts.mjs";
2
+ import { Dot } from "./Dot.mjs";
3
+ import { View } from "react-native-unistyles/components/native/View";
4
+ import { Text } from "react-native-unistyles/components/native/Text";
5
+ import { StyleSheet } from "react-native-unistyles";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+
8
+ //#region src/components/AgentAvatar.tsx
9
+ /** One letter for a first name, two for a hyphenated agent name (`po-developer` is PD). */
10
+ function agentInitials(name) {
11
+ return name.split(/[-_.\s]+/u).filter(Boolean).slice(0, 2).map((part) => part.charAt(0).toUpperCase()).join("");
12
+ }
13
+ const TINTS = [
14
+ "green",
15
+ "teal",
16
+ "blue",
17
+ "cyan",
18
+ "yellow",
19
+ "violet"
20
+ ];
21
+ function tintFor(seed) {
22
+ let hash = 0;
23
+ for (const char of seed) hash = (hash * 31 + char.charCodeAt(0)) % 997;
24
+ return TINTS[hash % TINTS.length] ?? "green";
25
+ }
26
+ const RING = 2;
27
+ const STATE_DOT = {
28
+ running: "green",
29
+ waiting: "yellow",
30
+ done: "neutral",
31
+ failed: "red"
32
+ };
33
+ /** An agent's face: its initials on a tint fixed by `seed`, with its state as a corner dot. */
34
+ function AgentAvatar({ name, seed = name, state, size = "md" }) {
35
+ return /* @__PURE__ */ jsxs(View, {
36
+ accessible: true,
37
+ accessibilityRole: "image",
38
+ accessibilityLabel: state ? `${name}, ${state}` : name,
39
+ testID: "agent-avatar",
40
+ style: styles.frame,
41
+ children: [/* @__PURE__ */ jsx(View, {
42
+ style: styles.face(tintFor(seed), size === "sm" ? 24 : 32),
43
+ children: /* @__PURE__ */ jsx(Text, {
44
+ style: styles.initials,
45
+ children: agentInitials(name)
46
+ })
47
+ }), state ? /* @__PURE__ */ jsx(Dot, {
48
+ tone: STATE_DOT[state],
49
+ size: "md",
50
+ pulse: state === "running",
51
+ style: styles.dot
52
+ }) : null]
53
+ });
54
+ }
55
+ const styles = StyleSheet.create((theme) => ({
56
+ frame: { flexShrink: 0 },
57
+ face: (tint, side) => ({
58
+ width: side,
59
+ height: side,
60
+ alignItems: "center",
61
+ justifyContent: "center",
62
+ borderRadius: theme.borderRadius.full,
63
+ backgroundColor: theme.colors[`tint-${tint}`],
64
+ uni__dependencies: [0]
65
+ }),
66
+ initials: {
67
+ fontFamily: fontOf(theme.fontFamily.sans),
68
+ fontSize: theme.fontSize.sm,
69
+ lineHeight: theme.lineHeight.sm,
70
+ color: theme.colors.hi,
71
+ uni__dependencies: [0]
72
+ },
73
+ dot: {
74
+ position: "absolute",
75
+ right: -4,
76
+ bottom: -4,
77
+ width: theme.spacing(2) + 4,
78
+ height: theme.spacing(2) + 4,
79
+ borderWidth: RING,
80
+ borderColor: theme.colors.panel,
81
+ uni__dependencies: [0]
82
+ }
83
+ }));
84
+
85
+ //#endregion
86
+ export { AgentAvatar, agentInitials };
87
+ //# sourceMappingURL=AgentAvatar.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentAvatar.mjs","names":["DotTone","StyleSheet","fontOf","Dot","AgentState","agentInitials","name","split","filter","Boolean","slice","map","part","charAt","toUpperCase","join","TINTS","const","Tint","tintFor","seed","hash","char","charCodeAt","length","RING","STATE_DOT","Readonly","Record","running","waiting","done","failed","AgentAvatar","state","size","styles","frame","face","initials","dot","create","theme","flexShrink","tint","side","width","height","alignItems","justifyContent","borderRadius","full","backgroundColor","colors","uni__dependencies","fontFamily","sans","fontSize","sm","lineHeight","color","hi","position","right","bottom","spacing","borderWidth","borderColor","panel"],"sources":["../../src/components/AgentAvatar.tsx"],"sourcesContent":["import type { DotTone } from '@agimon-ai/po-web-components/nativeTheme';\nimport { Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { fontOf } from '../lib/fonts';\nimport { Dot } from './Dot';\n\n/** How an agent is doing, as the avatar's corner dot shows it. */\nexport type AgentState = 'running' | 'waiting' | 'done' | 'failed';\n\n/** One letter for a first name, two for a hyphenated agent name (`po-developer` is PD). */\nexport function agentInitials(name: string): string {\n return name\n .split(/[-_.\\s]+/u)\n .filter(Boolean)\n .slice(0, 2)\n .map((part) => part.charAt(0).toUpperCase())\n .join('');\n}\n\n// ponytail: mirrors po-web-components AgentAvatar's tint hash; keep the two in step so a seed reads the same tint.\nconst TINTS = ['green', 'teal', 'blue', 'cyan', 'yellow', 'violet'] as const;\ntype Tint = (typeof TINTS)[number];\n\nfunction tintFor(seed: string): Tint {\n let hash = 0;\n for (const char of seed) hash = (hash * 31 + char.charCodeAt(0)) % 997;\n return TINTS[hash % TINTS.length] ?? 'green';\n}\n\nconst RING = 2;\n\nconst STATE_DOT: Readonly<Record<AgentState, DotTone>> = {\n running: 'green',\n waiting: 'yellow',\n done: 'neutral',\n failed: 'red',\n};\n\n/** An agent's face: its initials on a tint fixed by `seed`, with its state as a corner dot. */\nexport function AgentAvatar({\n name,\n seed = name,\n state,\n size = 'md',\n}: {\n name: string;\n seed?: string;\n state?: AgentState;\n size?: 'sm' | 'md';\n}) {\n return (\n <View\n accessible\n accessibilityRole=\"image\"\n accessibilityLabel={state ? `${name}, ${state}` : name}\n testID=\"agent-avatar\"\n style={styles.frame}\n >\n <View style={styles.face(tintFor(seed), size === 'sm' ? 24 : 32)}>\n <Text style={styles.initials}>{agentInitials(name)}</Text>\n </View>\n {state ? <Dot tone={STATE_DOT[state]} size=\"md\" pulse={state === 'running'} style={styles.dot} /> : null}\n </View>\n );\n}\n\nconst styles = StyleSheet.create((theme) => ({\n frame: { flexShrink: 0 },\n // `size-6` and `size-8`.\n face: (tint: Tint, side: number) => ({\n width: side,\n height: side,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.borderRadius.full,\n backgroundColor: theme.colors[`tint-${tint}`],\n }),\n initials: {\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n color: theme.colors.hi,\n },\n // `-right-0.5 -bottom-0.5 ring-2 ring-po-panel`: a ring sits outside the dot, a border inside, so the box grows by it.\n dot: {\n position: 'absolute',\n right: -2 - RING,\n bottom: -2 - RING,\n width: theme.spacing(2) + 2 * RING,\n height: theme.spacing(2) + 2 * RING,\n borderWidth: RING,\n borderColor: theme.colors.panel,\n },\n}));\n"],"mappings":";;;;;;;;;AAWA,SAAgBK,cAAcC,MAAsB;CAClD,OAAOA,KACJC,MAAM,WAAW,CAAC,CAClBC,OAAOC,OAAO,CAAC,CACfC,MAAM,GAAG,CAAC,CAAC,CACXC,KAAKC,SAASA,KAAKC,OAAO,CAAC,CAAC,CAACC,YAAY,CAAC,CAAC,CAC3CC,KAAK,EAAE;AACZ;AAGA,MAAMC,QAAQ;CAAC;CAAS;CAAQ;CAAQ;CAAQ;CAAU;AAAQ;AAGlE,SAASG,QAAQC,MAAoB;CACnC,IAAIC,OAAO;CACX,KAAK,MAAMC,QAAQF,MAAMC,QAAQA,OAAO,KAAKC,KAAKC,WAAW,CAAC,KAAK;CACnE,OAAOP,MAAMK,OAAOL,MAAMQ,WAAW;AACvC;AAEA,MAAMC,OAAO;AAEb,MAAMC,YAAmD;CACvDG,SAAS;CACTC,SAAS;CACTC,MAAM;CACNC,QAAQ;AACV;;AAGA,SAAgBC,YAAY,EAC1B3B,MACAc,OAAOd,MACP4B,OACAC,OAAO,QAMN;CACD,OACE,qBAAC,MAAD;EACE;EACA,mBAAkB;EAClB,oBAAoBD,QAAQ,GAAG5B,KAAI,IAAK4B,UAAU5B;EAClD,QAAO;EACP,OAAO8B,OAAOC;YALhB,CAOE,oBAAC,MAAD;GAAM,OAAOD,OAAOE,KAAKnB,QAAQC,IAAI,GAAGe,SAAS,OAAO,KAAK,EAAE;aAC7D,oBAAC,MAAD;IAAM,OAAOC,OAAOG;cAAWlC,cAAcC,IAAI;GAAQ;EACrD,IACL4B,QAAQ,oBAAC,KAAD;GAAK,MAAMR,UAAUQ;GAAQ,MAAK;GAAK,OAAOA,UAAU;GAAW,OAAOE,OAAOI;EAAI,KAAM,IAChG;;AAEV;AAEA,MAAMJ,SAASnC,WAAWwC,QAAQC,WAAW;CAC3CL,OAAO,EAAEM,YAAY,EAAE;CAEvBL,OAAOM,MAAYC,UAAkB;EACnCC,OAAOD;EACPE,QAAQF;EACRG,YAAY;EACZC,gBAAgB;EAChBC,cAAcR,MAAMQ,aAAaC;EACjCC,iBAAiBV,MAAMW,OAAO,QAAQT;EAAOU,mBAAA,CAAA,CAAA;CAC/C;CACAf,UAAU;EACRgB,YAAYrD,OAAOwC,MAAMa,WAAWC,IAAI;EACxCC,UAAUf,MAAMe,SAASC;EACzBC,YAAYjB,MAAMiB,WAAWD;EAC7BE,OAAOlB,MAAMW,OAAOQ;EAAEP,mBAAA,CAAA,CAAA;CACxB;CAEAd,KAAK;EACHsB,UAAU;EACVC,OAAO;EACPC,QAAQ;EACRlB,OAAOJ,MAAMuB,QAAQ,CAAC,IAAI;EAC1BlB,QAAQL,MAAMuB,QAAQ,CAAC,IAAI;EAC3BC,aAAazC;EACb0C,aAAazB,MAAMW,OAAOe;EAAKd,mBAAA,CAAA,CAAA;CACjC;AACF,EAAE"}
@@ -1,5 +1,5 @@
1
- import { ChipTone } from "@agimon-ai/po-web-components/nativeTheme";
2
1
  import { StyleProp, ViewProps, ViewStyle } from "react-native";
2
+ import { ChipTone } from "@agimon-ai/po-web-components/nativeTheme";
3
3
  import { ReactNode } from "react";
4
4
  //#region src/components/Badge.d.ts
5
5
  export type BadgeTone = ChipTone;
@@ -2,8 +2,8 @@ import { fontOf } from "../lib/fonts.mjs";
2
2
  import { CHIP_INK } from "../lib/tone.mjs";
3
3
  import { wrapText } from "../lib/wrapText.mjs";
4
4
  import { View } from "react-native-unistyles/components/native/View";
5
- import { withAlpha } from "@agimon-ai/po-web-components/nativeTheme";
6
5
  import { StyleSheet, useUnistyles } from "react-native-unistyles";
6
+ import { withAlpha } from "@agimon-ai/po-web-components/nativeTheme";
7
7
  import { jsx } from "react/jsx-runtime";
8
8
 
9
9
  //#region src/components/Badge.tsx
@@ -3,8 +3,8 @@ import { wrapText } from "../lib/wrapText.mjs";
3
3
  import { IconColorContext } from "../icons/IconColorContext.mjs";
4
4
  import { impactLight } from "../lib/haptics.mjs";
5
5
  import { Spinner } from "./Spinner.mjs";
6
- import { withAlpha } from "@agimon-ai/po-web-components/nativeTheme";
7
6
  import { StyleSheet, useUnistyles } from "react-native-unistyles";
7
+ import { withAlpha } from "@agimon-ai/po-web-components/nativeTheme";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  import { Pressable } from "react-native-unistyles/components/native/Pressable";
10
10
 
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlock.d.mts","names":[],"sources":["../../src/components/CodeBlock.tsx"],"mappings":";iBA4BiB;;EAEf;;EAEA;;wBAGc,YAAY,MAAM,YAAY,iCAAc,IAAA"}
1
+ {"version":3,"file":"CodeBlock.d.mts","names":[],"sources":["../../src/components/CodeBlock.tsx"],"mappings":";iBAciB;;EAEf;;EAEA;;wBAGc,YAAY,MAAM,YAAY,iCAAc,IAAA"}
@@ -1,30 +1,13 @@
1
1
  import { fontOf } from "../lib/fonts.mjs";
2
- import { Button } from "./Button.mjs";
3
- import { CheckIcon, CopyIcon } from "../icons/icons.mjs";
2
+ import { CopyButton } from "./CopyButton.mjs";
4
3
  import { View } from "react-native-unistyles/components/native/View";
5
- import { StyleSheet } from "react-native-unistyles";
6
4
  import { Text } from "react-native-unistyles/components/native/Text";
7
- import { useEffect, useState } from "react";
5
+ import { StyleSheet } from "react-native-unistyles";
8
6
  import { jsx, jsxs } from "react/jsx-runtime";
9
7
  import { ScrollView } from "react-native-unistyles/components/native/ScrollView";
10
- import { setStringAsync } from "expo-clipboard";
11
8
 
12
9
  //#region src/components/CodeBlock.tsx
13
- /** How long the control stays acknowledged before returning to its icon. */
14
- const FEEDBACK_MS = 1500;
15
- const LABELS = {
16
- idle: "copy",
17
- copied: "copied",
18
- failed: "copying failed"
19
- };
20
10
  function CodeBlock({ text, language }) {
21
- const [state, setState] = useState("idle");
22
- useEffect(() => {
23
- if (state === "idle") return void 0;
24
- const timer = setTimeout(() => setState("idle"), FEEDBACK_MS);
25
- return () => clearTimeout(timer);
26
- }, [state]);
27
- const label = LABELS[state];
28
11
  return /* @__PURE__ */ jsxs(View, {
29
12
  style: styles.block,
30
13
  accessibilityLabel: language === void 0 ? void 0 : `${language} code`,
@@ -37,21 +20,9 @@ function CodeBlock({ text, language }) {
37
20
  style: styles.code,
38
21
  children: text
39
22
  })
40
- }), /* @__PURE__ */ jsx(Button, {
41
- variant: "ghost",
42
- size: "icon",
43
- accessibilityLabel: label,
44
- onPress: () => {
45
- setStringAsync(text).then((saved) => setState(saved ? "copied" : "failed"), () => setState("failed"));
46
- },
47
- style: styles.copy,
48
- children: state === "copied" ? /* @__PURE__ */ jsx(CheckIcon, {
49
- size: 12,
50
- color: "green"
51
- }) : /* @__PURE__ */ jsx(CopyIcon, {
52
- size: 12,
53
- color: "faint"
54
- })
23
+ }), /* @__PURE__ */ jsx(CopyButton, {
24
+ text,
25
+ style: styles.copy
55
26
  })]
56
27
  });
57
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlock.mjs","names":["setStringAsync","useEffect","useState","StyleSheet","CheckIcon","CopyIcon","fontOf","Button","FEEDBACK_MS","CopyState","LABELS","Readonly","Record","idle","copied","failed","CodeBlockProps","text","language","CodeBlock","state","setState","undefined","timer","setTimeout","clearTimeout","label","styles","block","scroll","code","then","saved","copy","create","theme","borderRadius","DEFAULT","borderWidth","borderColor","colors","border","backgroundColor","deep","uni__dependencies","padding","spacing","paddingRight","color","fontFamily","mono","fontSize","sm","lineHeight","lineHeightRelaxed","position","top","right"],"sources":["../../src/components/CodeBlock.tsx"],"sourcesContent":["import { setStringAsync } from 'expo-clipboard';\nimport { useEffect, useState } from 'react';\nimport { ScrollView, Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { CheckIcon, CopyIcon } from '../icons/icons';\nimport { fontOf } from '../lib/fonts';\nimport { Button } from './Button';\n\n/*\n * A fenced code block: the source in selectable mono type that scrolls\n * sideways rather than wrapping, and a copy button that says when it worked.\n *\n * ponytail: no syntax colouring and no mermaid diagrams yet; a mermaid fence\n * shows its source, which is still the whole content.\n */\n\n/** How long the control stays acknowledged before returning to its icon. */\nconst FEEDBACK_MS = 1500;\n\ntype CopyState = 'idle' | 'copied' | 'failed';\n\nconst LABELS: Readonly<Record<CopyState, string>> = {\n idle: 'copy',\n copied: 'copied',\n failed: 'copying failed',\n};\n\nexport interface CodeBlockProps {\n /** The block's source, without the fence lines. */\n text: string;\n /** The fence's language word, such as `ts`; the web reads it from a `language-*` class. */\n language?: string;\n}\n\nexport function CodeBlock({ text, language }: CodeBlockProps) {\n const [state, setState] = useState<CopyState>('idle');\n\n useEffect(() => {\n if (state === 'idle') return undefined;\n const timer = setTimeout(() => setState('idle'), FEEDBACK_MS);\n return () => clearTimeout(timer);\n }, [state]);\n\n const label = LABELS[state];\n return (\n <View style={styles.block} accessibilityLabel={language === undefined ? undefined : `${language} code`}>\n <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.scroll}>\n <Text selectable style={styles.code}>\n {text}\n </Text>\n </ScrollView>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n accessibilityLabel={label}\n onPress={() => {\n setStringAsync(text).then(\n (saved) => setState(saved ? 'copied' : 'failed'),\n () => setState('failed'),\n );\n }}\n style={styles.copy}\n >\n {state === 'copied' ? <CheckIcon size={12} color=\"green\" /> : <CopyIcon size={12} color=\"faint\" />}\n </Button>\n </View>\n );\n}\n\nconst styles = StyleSheet.create((theme) => ({\n block: {\n borderRadius: theme.borderRadius.DEFAULT,\n borderWidth: 1,\n borderColor: theme.colors.border,\n backgroundColor: theme.colors.deep,\n },\n // The right padding keeps the first line clear of the copy button.\n scroll: { padding: theme.spacing(2.5), paddingRight: theme.spacing(9) },\n code: {\n color: theme.colors.text,\n fontFamily: fontOf(theme.fontFamily.mono),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeightRelaxed.sm,\n },\n copy: { position: 'absolute', top: theme.spacing(1.5), right: theme.spacing(1.5) },\n}));\n"],"mappings":";;;;;;;;;;;;;AAkBA,MAAMQ,cAAc;AAIpB,MAAME,SAA8C;CAClDG,MAAM;CACNC,QAAQ;CACRC,QAAQ;AACV;AASA,SAAgBI,UAAU,EAAEF,MAAMC,YAA4B;CAC5D,MAAM,CAACE,OAAOC,YAAYnB,SAAoB,MAAM;CAEpDD,gBAAgB;EACd,IAAImB,UAAU,QAAQ,OAAOE;EAC7B,MAAMC,QAAQC,iBAAiBH,SAAS,MAAM,GAAGb,WAAW;EAC5D,aAAaiB,aAAaF,KAAK;CACjC,GAAG,CAACH,KAAK,CAAC;CAEV,MAAMM,QAAQhB,OAAOU;CACrB,OACE,qBAAC,MAAD;EAAM,OAAOO,OAAOC;EAAO,oBAAoBV,aAAaI,SAAYA,SAAY,GAAGJ,SAAQ;YAA/F,CACE,oBAAC,YAAD;GAAY;GAAW,gCAAgC;GAAO,uBAAuBS,OAAOE;aAC1F,oBAAC,MAAD;IAAM;IAAW,OAAOF,OAAOG;cAC5Bb;GACG;EACI,IACZ,oBAAC,QAAD;GACE,SAAQ;GACR,MAAK;GACL,oBAAoBS;GACpB,eAAe;IACb1B,eAAeiB,IAAI,CAAC,CAACc,MAClBC,UAAUX,SAASW,QAAQ,WAAW,QAAQ,SACzCX,SAAS,QAAQ,CACzB;GACF;GACA,OAAOM,OAAOM;aAEbb,UAAU,WAAW,oBAAC,WAAD;IAAW,MAAM;IAAI,OAAM;GAAO,KAAM,oBAAC,UAAD;IAAU,MAAM;IAAI,OAAM;GAAO;EACzF,EACJ;;AAEV;AAEA,MAAMO,SAASxB,WAAW+B,QAAQC,WAAW;CAC3CP,OAAO;EACLQ,cAAcD,MAAMC,aAAaC;EACjCC,aAAa;EACbC,aAAaJ,MAAMK,OAAOC;EAC1BC,iBAAiBP,MAAMK,OAAOG;EAAIC,mBAAA,CAAA,CAAA;CACpC;CAEAf,QAAQ;EAAEgB,SAASV,MAAMW,QAAQ,GAAG;EAAGC,cAAcZ,MAAMW,QAAQ,CAAC;EAACF,mBAAA,CAAA,CAAA;CAAC;CACtEd,MAAM;EACJkB,OAAOb,MAAMK,OAAOvB;EACpBgC,YAAY3C,OAAO6B,MAAMc,WAAWC,IAAI;EACxCC,UAAUhB,MAAMgB,SAASC;EACzBC,YAAYlB,MAAMmB,kBAAkBF;EAAER,mBAAA,CAAA,CAAA;CACxC;CACAX,MAAM;EAAEsB,UAAU;EAAYC,KAAKrB,MAAMW,QAAQ,GAAG;EAAGW,OAAOtB,MAAMW,QAAQ,GAAG;EAACF,mBAAA,CAAA,CAAA;CAAC;AACnF,EAAE"}
1
+ {"version":3,"file":"CodeBlock.mjs","names":["StyleSheet","fontOf","CopyButton","CodeBlockProps","text","language","CodeBlock","styles","block","undefined","scroll","code","copy","create","theme","borderRadius","DEFAULT","borderWidth","borderColor","colors","border","backgroundColor","deep","uni__dependencies","padding","spacing","paddingRight","color","fontFamily","mono","fontSize","sm","lineHeight","lineHeightRelaxed","position","top","right"],"sources":["../../src/components/CodeBlock.tsx"],"sourcesContent":["import { ScrollView, Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { fontOf } from '../lib/fonts';\nimport { CopyButton } from './CopyButton';\n\n/*\n * A fenced code block: the source in selectable mono type that scrolls\n * sideways rather than wrapping, and a copy button that says when it worked.\n *\n * ponytail: no syntax colouring and no mermaid diagrams yet; a mermaid fence\n * shows its source, which is still the whole content.\n */\n\nexport interface CodeBlockProps {\n /** The block's source, without the fence lines. */\n text: string;\n /** The fence's language word, such as `ts`; the web reads it from a `language-*` class. */\n language?: string;\n}\n\nexport function CodeBlock({ text, language }: CodeBlockProps) {\n return (\n <View style={styles.block} accessibilityLabel={language === undefined ? undefined : `${language} code`}>\n <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.scroll}>\n <Text selectable style={styles.code}>\n {text}\n </Text>\n </ScrollView>\n <CopyButton text={text} style={styles.copy} />\n </View>\n );\n}\n\nconst styles = StyleSheet.create((theme) => ({\n block: {\n borderRadius: theme.borderRadius.DEFAULT,\n borderWidth: 1,\n borderColor: theme.colors.border,\n backgroundColor: theme.colors.deep,\n },\n // The right padding keeps the first line clear of the copy button.\n scroll: { padding: theme.spacing(2.5), paddingRight: theme.spacing(9) },\n code: {\n color: theme.colors.text,\n fontFamily: fontOf(theme.fontFamily.mono),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeightRelaxed.sm,\n },\n copy: { position: 'absolute', top: theme.spacing(1.5), right: theme.spacing(1.5) },\n}));\n"],"mappings":";;;;;;;;;AAqBA,SAAgBM,UAAU,EAAEF,MAAMC,YAA4B;CAC5D,OACE,qBAAC,MAAD;EAAM,OAAOE,OAAOC;EAAO,oBAAoBH,aAAaI,SAAYA,SAAY,GAAGJ,SAAQ;YAA/F,CACE,oBAAC,YAAD;GAAY;GAAW,gCAAgC;GAAO,uBAAuBE,OAAOG;aAC1F,oBAAC,MAAD;IAAM;IAAW,OAAOH,OAAOI;cAC5BP;GACG;EACI,IACZ,oBAAC,YAAD;GAAkBA;GAAM,OAAOG,OAAOK;EAAK,EACvC;;AAEV;AAEA,MAAML,SAASP,WAAWa,QAAQC,WAAW;CAC3CN,OAAO;EACLO,cAAcD,MAAMC,aAAaC;EACjCC,aAAa;EACbC,aAAaJ,MAAMK,OAAOC;EAC1BC,iBAAiBP,MAAMK,OAAOG;EAAIC,mBAAA,CAAA,CAAA;CACpC;CAEAb,QAAQ;EAAEc,SAASV,MAAMW,QAAQ,GAAG;EAAGC,cAAcZ,MAAMW,QAAQ,CAAC;EAACF,mBAAA,CAAA,CAAA;CAAC;CACtEZ,MAAM;EACJgB,OAAOb,MAAMK,OAAOf;EACpBwB,YAAY3B,OAAOa,MAAMc,WAAWC,IAAI;EACxCC,UAAUhB,MAAMgB,SAASC;EACzBC,YAAYlB,MAAMmB,kBAAkBF;EAAER,mBAAA,CAAA,CAAA;CACxC;CACAX,MAAM;EAAEsB,UAAU;EAAYC,KAAKrB,MAAMW,QAAQ,GAAG;EAAGW,OAAOtB,MAAMW,QAAQ,GAAG;EAACF,mBAAA,CAAA,CAAA;CAAC;AACnF,EAAE"}
@@ -0,0 +1,13 @@
1
+ import { StyleProp, ViewStyle } from "react-native";
2
+ //#region src/components/CopyButton.d.ts
3
+ export interface CopyButtonProps {
4
+ /** The text the press puts on the clipboard. */
5
+ text: string;
6
+ /** What is copied, announced before the state, such as `path`. */
7
+ label?: string;
8
+ style?: StyleProp<ViewStyle>;
9
+ }
10
+ /** A quiet icon button that copies text and says, briefly, whether it worked. */
11
+ export declare function CopyButton({ text, label, style }: CopyButtonProps): import("react").JSX.Element;
12
+ //#endregion
13
+ //# sourceMappingURL=CopyButton.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CopyButton.d.mts","names":[],"sources":["../../src/components/CopyButton.tsx"],"mappings":";;iBAkBiB;;EAEf;;EAEA;EACA,QAAQ,UAAU;;;wBAIJ,aAAa,MAAM,OAAO,SAAS,kCAAe,IAAA"}
@@ -0,0 +1,43 @@
1
+ import { Button } from "./Button.mjs";
2
+ import { CheckIcon, CopyIcon } from "../icons/icons.mjs";
3
+ import { useEffect, useState } from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import { setStringAsync } from "expo-clipboard";
6
+
7
+ //#region src/components/CopyButton.tsx
8
+ /** How long the control stays acknowledged before returning to its icon. */
9
+ const FEEDBACK_MS = 1500;
10
+ const LABELS = {
11
+ idle: "copy",
12
+ copied: "copied",
13
+ failed: "copying failed"
14
+ };
15
+ /** A quiet icon button that copies text and says, briefly, whether it worked. */
16
+ function CopyButton({ text, label, style }) {
17
+ const [state, setState] = useState("idle");
18
+ useEffect(() => {
19
+ if (state === "idle") return void 0;
20
+ const timer = setTimeout(() => setState("idle"), FEEDBACK_MS);
21
+ return () => clearTimeout(timer);
22
+ }, [state]);
23
+ return /* @__PURE__ */ jsx(Button, {
24
+ variant: "ghost",
25
+ size: "icon",
26
+ accessibilityLabel: label === void 0 ? LABELS[state] : `${LABELS[state]} ${label}`,
27
+ onPress: () => {
28
+ setStringAsync(text).then((saved) => setState(saved ? "copied" : "failed"), () => setState("failed"));
29
+ },
30
+ style,
31
+ children: state === "copied" ? /* @__PURE__ */ jsx(CheckIcon, {
32
+ size: 12,
33
+ color: "green"
34
+ }) : /* @__PURE__ */ jsx(CopyIcon, {
35
+ size: 12,
36
+ color: "faint"
37
+ })
38
+ });
39
+ }
40
+
41
+ //#endregion
42
+ export { CopyButton };
43
+ //# sourceMappingURL=CopyButton.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CopyButton.mjs","names":["setStringAsync","useEffect","useState","StyleProp","ViewStyle","CheckIcon","CopyIcon","Button","FEEDBACK_MS","CopyState","LABELS","Readonly","Record","idle","copied","failed","CopyButtonProps","text","label","style","CopyButton","state","setState","undefined","timer","setTimeout","clearTimeout","then","saved"],"sources":["../../src/components/CopyButton.tsx"],"sourcesContent":["import { setStringAsync } from 'expo-clipboard';\nimport { useEffect, useState } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\n\nimport { CheckIcon, CopyIcon } from '../icons/icons';\nimport { Button } from './Button';\n\n/** How long the control stays acknowledged before returning to its icon. */\nconst FEEDBACK_MS = 1500;\n\ntype CopyState = 'idle' | 'copied' | 'failed';\n\nconst LABELS: Readonly<Record<CopyState, string>> = {\n idle: 'copy',\n copied: 'copied',\n failed: 'copying failed',\n};\n\nexport interface CopyButtonProps {\n /** The text the press puts on the clipboard. */\n text: string;\n /** What is copied, announced before the state, such as `path`. */\n label?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/** A quiet icon button that copies text and says, briefly, whether it worked. */\nexport function CopyButton({ text, label, style }: CopyButtonProps) {\n const [state, setState] = useState<CopyState>('idle');\n\n useEffect(() => {\n if (state === 'idle') return undefined;\n const timer = setTimeout(() => setState('idle'), FEEDBACK_MS);\n return () => clearTimeout(timer);\n }, [state]);\n\n return (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n accessibilityLabel={label === undefined ? LABELS[state] : `${LABELS[state]} ${label}`}\n onPress={() => {\n setStringAsync(text).then(\n (saved) => setState(saved ? 'copied' : 'failed'),\n () => setState('failed'),\n );\n }}\n style={style}\n >\n {state === 'copied' ? <CheckIcon size={12} color=\"green\" /> : <CopyIcon size={12} color=\"faint\" />}\n </Button>\n );\n}\n"],"mappings":";;;;;;;;AAQA,MAAMQ,cAAc;AAIpB,MAAME,SAA8C;CAClDG,MAAM;CACNC,QAAQ;CACRC,QAAQ;AACV;;AAWA,SAAgBK,WAAW,EAAEH,MAAMC,OAAOC,SAA0B;CAClE,MAAM,CAACE,OAAOC,YAAYpB,SAAoB,MAAM;CAEpDD,gBAAgB;EACd,IAAIoB,UAAU,QAAQ,OAAOE;EAC7B,MAAMC,QAAQC,iBAAiBH,SAAS,MAAM,GAAGd,WAAW;EAC5D,aAAakB,aAAaF,KAAK;CACjC,GAAG,CAACH,KAAK,CAAC;CAEV,OACE,oBAAC,QAAD;EACE,SAAQ;EACR,MAAK;EACL,oBAAoBH,UAAUK,SAAYb,OAAOW,SAAS,GAAGX,OAAOW,OAAM,GAAIH;EAC9E,eAAe;GACblB,eAAeiB,IAAI,CAAC,CAACU,MAClBC,UAAUN,SAASM,QAAQ,WAAW,QAAQ,SACzCN,SAAS,QAAQ,CACzB;EACF;EACOH;YAENE,UAAU,WAAW,oBAAC,WAAD;GAAW,MAAM;GAAI,OAAM;EAAO,KAAM,oBAAC,UAAD;GAAU,MAAM;GAAI,OAAM;EAAO;CACzF;AAEZ"}
@@ -4,14 +4,14 @@ import { Button } from "./Button.mjs";
4
4
  import { CloseIcon } from "../icons/icons.mjs";
5
5
  import { useControllable } from "../lib/useControllable.mjs";
6
6
  import { View } from "react-native-unistyles/components/native/View";
7
- import { withAlpha } from "@agimon-ai/po-web-components/nativeTheme";
7
+ import { Text } from "react-native-unistyles/components/native/Text";
8
8
  import { StyleSheet, useUnistyles } from "react-native-unistyles";
9
9
  import { Easing, Modal, Platform } from "react-native";
10
- import { Text } from "react-native-unistyles/components/native/Text";
10
+ import { Animated } from "react-native-unistyles/components/native/Animated";
11
+ import { withAlpha } from "@agimon-ai/po-web-components/nativeTheme";
11
12
  import { createContext, useContext, useEffect, useRef } from "react";
12
13
  import { jsx, jsxs } from "react/jsx-runtime";
13
14
  import { Pressable } from "react-native-unistyles/components/native/Pressable";
14
- import { Animated } from "react-native-unistyles/components/native/Animated";
15
15
 
16
16
  //#region src/components/Dialog.tsx
17
17
  /** `bg-po-deep/70`. */
@@ -1,5 +1,5 @@
1
- import { DotTone } from "@agimon-ai/po-web-components/nativeTheme";
2
1
  import { StyleProp, ViewStyle } from "react-native";
2
+ import { DotTone } from "@agimon-ai/po-web-components/nativeTheme";
3
3
  //#region src/components/Dot.d.ts
4
4
  type DotTone$1 = DotTone;
5
5
  export type DotSize = 'xs' | 'sm' | 'md' | 'lg';
@@ -1,8 +1,8 @@
1
- import { withAlpha } from "@agimon-ai/po-web-components/nativeTheme";
2
1
  import { StyleSheet, useUnistyles } from "react-native-unistyles";
2
+ import { Animated } from "react-native-unistyles/components/native/Animated";
3
+ import { withAlpha } from "@agimon-ai/po-web-components/nativeTheme";
3
4
  import { useEffect, useRef } from "react";
4
5
  import { jsx } from "react/jsx-runtime";
5
- import { Animated } from "react-native-unistyles/components/native/Animated";
6
6
 
7
7
  //#region src/components/Dot.tsx
8
8
  /** `bg-po-faint/40`: a thing that exists but is not live. */
@@ -6,8 +6,8 @@ import { CheckIcon } from "../icons/icons.mjs";
6
6
  import { useControllable } from "../lib/useControllable.mjs";
7
7
  import { SheetFrame } from "./SheetFrame.mjs";
8
8
  import { View } from "react-native-unistyles/components/native/View";
9
- import { StyleSheet } from "react-native-unistyles";
10
9
  import { Text } from "react-native-unistyles/components/native/Text";
10
+ import { StyleSheet } from "react-native-unistyles";
11
11
  import { createContext, useContext } from "react";
12
12
  import { jsx, jsxs } from "react/jsx-runtime";
13
13
  import { Pressable } from "react-native-unistyles/components/native/Pressable";
@@ -0,0 +1,13 @@
1
+ import { PresentedLine } from "@agimon-ai/po-web-components/text";
2
+ import { ViewProps } from "react-native";
3
+ //#region src/components/HashlineLines.d.ts
4
+ export interface HashlineLinesProps extends Omit<ViewProps, 'children'> {
5
+ lines: readonly PresentedLine[];
6
+ /** Width of the line-number column, in characters, from `hashlineBody`. */
7
+ gutter: number;
8
+ /** The file a body of bare anchored lines came from, for highlighting. */
9
+ path?: string | undefined;
10
+ }
11
+ export declare function HashlineLines({ lines, gutter, path, style, ...props }: HashlineLinesProps): import("react").JSX.Element;
12
+ //#endregion
13
+ //# sourceMappingURL=HashlineLines.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HashlineLines.d.mts","names":[],"sources":["../../src/components/HashlineLines.tsx"],"mappings":";;;iBA0BiB,2BAA2B,KAAK;EAC/C,gBAAgB;;EAEhB;;EAEA;;wBAsCc,gBAAgB,OAAO,QAAQ,MAAM,UAAU,SAAS,qCAAkB,IAAA"}
@@ -0,0 +1,137 @@
1
+ import { fontOf } from "../lib/fonts.mjs";
2
+ import { SyntaxLine } from "./SyntaxLine.mjs";
3
+ import { detectGrammar, hashlineGroups, hashlineGroupsKey, highlightToLines } from "@agimon-ai/po-web-components/text";
4
+ import { View } from "react-native-unistyles/components/native/View";
5
+ import { Text } from "react-native-unistyles/components/native/Text";
6
+ import { StyleSheet } from "react-native-unistyles";
7
+ import { useEffect, useMemo, useState } from "react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+
10
+ //#region src/components/HashlineLines.tsx
11
+ const NO_SPANS = /* @__PURE__ */ new Map();
12
+ /** Spans per presented-line index, kept with the body they belong to, as the web's hook. */
13
+ function useHashlineSpans(lines, path) {
14
+ const groups = useMemo(() => hashlineGroups(lines, path), [lines, path]);
15
+ const key = hashlineGroupsKey(groups);
16
+ const [done, setDone] = useState();
17
+ useEffect(() => {
18
+ let live = true;
19
+ const next = /* @__PURE__ */ new Map();
20
+ const work = groups.map(async (group) => {
21
+ const grammar = detectGrammar({
22
+ path: group.path,
23
+ text: group.text
24
+ });
25
+ if (grammar === void 0) return;
26
+ const highlighted = await highlightToLines(group.text, grammar);
27
+ if (highlighted === void 0) return;
28
+ for (const [offset, index] of group.indices.entries()) {
29
+ const line = highlighted[offset];
30
+ if (line !== void 0) next.set(index, line);
31
+ }
32
+ });
33
+ Promise.allSettled(work).then(() => {
34
+ if (live && next.size > 0) setDone({
35
+ key,
36
+ spans: next
37
+ });
38
+ });
39
+ return () => {
40
+ live = false;
41
+ };
42
+ }, [key]);
43
+ return done?.key === key ? done.spans : NO_SPANS;
44
+ }
45
+ function HashlineLines({ lines, gutter, path, style, ...props }) {
46
+ const spans = useHashlineSpans(lines, path);
47
+ return /* @__PURE__ */ jsx(View, {
48
+ style,
49
+ ...props,
50
+ children: lines.map((line, index) => {
51
+ if (line.type === "file") return /* @__PURE__ */ jsx(Text, {
52
+ numberOfLines: 1,
53
+ style: [
54
+ styles.mono,
55
+ styles.faint,
56
+ index > 0 && styles.fileGap
57
+ ],
58
+ children: line.path
59
+ }, index);
60
+ if (line.type === "plain") return /* @__PURE__ */ jsx(Text, {
61
+ selectable: true,
62
+ style: [styles.mono, styles.dim],
63
+ children: line.text
64
+ }, index);
65
+ const { value } = line;
66
+ const match = value.marker === "match";
67
+ return /* @__PURE__ */ jsxs(View, {
68
+ style: styles.row,
69
+ children: [
70
+ value.marker !== void 0 ? /* @__PURE__ */ jsx(Text, {
71
+ style: [styles.mono, match ? styles.marker : styles.faint],
72
+ children: match ? ">>" : " "
73
+ }) : null,
74
+ /* @__PURE__ */ jsx(Text, {
75
+ style: [styles.mono, styles.faint],
76
+ children: String(value.line).padStart(gutter)
77
+ }),
78
+ /* @__PURE__ */ jsx(SyntaxLine, {
79
+ selectable: true,
80
+ spans: spans.get(index),
81
+ text: value.content,
82
+ style: [
83
+ styles.mono,
84
+ styles.content,
85
+ match ? styles.hi : styles.text
86
+ ]
87
+ })
88
+ ]
89
+ }, index);
90
+ })
91
+ });
92
+ }
93
+ const styles = StyleSheet.create((theme) => ({
94
+ row: {
95
+ flexDirection: "row",
96
+ gap: theme.spacing(2),
97
+ uni__dependencies: [0]
98
+ },
99
+ fileGap: {
100
+ marginTop: theme.spacing(1),
101
+ uni__dependencies: [0]
102
+ },
103
+ mono: {
104
+ fontFamily: fontOf(theme.fontFamily.mono),
105
+ fontSize: theme.fontSize.sm,
106
+ lineHeight: theme.lineHeightRelaxed.sm,
107
+ uni__dependencies: [0]
108
+ },
109
+ content: {
110
+ flex: 1,
111
+ minWidth: 0
112
+ },
113
+ marker: {
114
+ color: theme.colors.blue,
115
+ uni__dependencies: [0]
116
+ },
117
+ faint: {
118
+ color: theme.colors.faint,
119
+ uni__dependencies: [0]
120
+ },
121
+ dim: {
122
+ color: theme.colors.dim,
123
+ uni__dependencies: [0]
124
+ },
125
+ text: {
126
+ color: theme.colors.text,
127
+ uni__dependencies: [0]
128
+ },
129
+ hi: {
130
+ color: theme.colors.hi,
131
+ uni__dependencies: [0]
132
+ }
133
+ }));
134
+
135
+ //#endregion
136
+ export { HashlineLines };
137
+ //# sourceMappingURL=HashlineLines.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HashlineLines.mjs","names":["detectGrammar","hashlineGroups","hashlineGroupsKey","highlightToLines","PresentedLine","SyntaxSpan","useEffect","useMemo","useState","ViewProps","StyleSheet","fontOf","SyntaxLine","HashlineLinesProps","Omit","lines","gutter","path","NO_SPANS","ReadonlyMap","Map","useHashlineSpans","groups","key","done","setDone","spans","live","next","work","map","group","grammar","text","undefined","highlighted","offset","index","indices","entries","line","set","Promise","allSettled","then","size","HashlineLines","style","props","type","styles","mono","faint","fileGap","dim","value","match","marker","row","String","padStart","get","content","hi","create","theme","flexDirection","gap","spacing","uni__dependencies","marginTop","fontFamily","fontSize","sm","lineHeight","lineHeightRelaxed","flex","minWidth","color","colors","blue"],"sources":["../../src/components/HashlineLines.tsx"],"sourcesContent":["import {\n detectGrammar,\n hashlineGroups,\n hashlineGroupsKey,\n highlightToLines,\n type PresentedLine,\n type SyntaxSpan,\n} from '@agimon-ai/po-web-components/text';\nimport { useEffect, useMemo, useState } from 'react';\nimport { Text, View, type ViewProps } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { fontOf } from '../lib/fonts';\nimport { SyntaxLine } from './SyntaxLine';\n\n/*\n * The anchored body shared by hashline results, as the web's HashlineLines: a\n * file heading per group, a right-aligned line-number gutter, and a `>>`\n * marker on grep matches. Mono type makes a padded number the gutter, so the\n * column needs no measuring.\n *\n * The anchored lines are coloured with the web's grammars and palette when the\n * body names a file, or when `path` names one. Colour arrives late and never\n * blocks the text: an unhighlighted body is the same body without the colours.\n */\n\nexport interface HashlineLinesProps extends Omit<ViewProps, 'children'> {\n lines: readonly PresentedLine[];\n /** Width of the line-number column, in characters, from `hashlineBody`. */\n gutter: number;\n /** The file a body of bare anchored lines came from, for highlighting. */\n path?: string | undefined;\n}\n\nconst NO_SPANS: ReadonlyMap<number, readonly SyntaxSpan[]> = new Map();\n\n/** Spans per presented-line index, kept with the body they belong to, as the web's hook. */\nfunction useHashlineSpans(lines: readonly PresentedLine[], path: string | undefined) {\n const groups = useMemo(() => hashlineGroups(lines, path), [lines, path]);\n const key = hashlineGroupsKey(groups);\n const [done, setDone] = useState<{ key: string; spans: ReadonlyMap<number, readonly SyntaxSpan[]> }>();\n\n useEffect(() => {\n let live = true;\n const next = new Map<number, readonly SyntaxSpan[]>();\n const work = groups.map(async (group) => {\n const grammar = detectGrammar({ path: group.path, text: group.text });\n if (grammar === undefined) return;\n const highlighted = await highlightToLines(group.text, grammar);\n if (highlighted === undefined) return;\n for (const [offset, index] of group.indices.entries()) {\n const line = highlighted[offset];\n if (line !== undefined) next.set(index, line);\n }\n });\n // A grammar that fails to load leaves its run plain: colour is decoration.\n void Promise.allSettled(work).then(() => {\n if (live && next.size > 0) setDone({ key, spans: next });\n });\n return () => {\n live = false;\n };\n // The groups are rebuilt with the lines; their key is what actually changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [key]);\n\n return done?.key === key ? done.spans : NO_SPANS;\n}\n\nexport function HashlineLines({ lines, gutter, path, style, ...props }: HashlineLinesProps) {\n const spans = useHashlineSpans(lines, path);\n return (\n <View style={style} {...props}>\n {lines.map((line, index) => {\n if (line.type === 'file') {\n return (\n <Text key={index} numberOfLines={1} style={[styles.mono, styles.faint, index > 0 && styles.fileGap]}>\n {line.path}\n </Text>\n );\n }\n if (line.type === 'plain') {\n return (\n <Text key={index} selectable style={[styles.mono, styles.dim]}>\n {line.text}\n </Text>\n );\n }\n const { value } = line;\n const match = value.marker === 'match';\n return (\n <View key={index} style={styles.row}>\n {value.marker !== undefined ? (\n <Text style={[styles.mono, match ? styles.marker : styles.faint]}>{match ? '>>' : ' '}</Text>\n ) : null}\n <Text style={[styles.mono, styles.faint]}>{String(value.line).padStart(gutter)}</Text>\n <SyntaxLine\n selectable\n spans={spans.get(index)}\n text={value.content}\n style={[styles.mono, styles.content, match ? styles.hi : styles.text]}\n />\n </View>\n );\n })}\n </View>\n );\n}\n\nconst styles = StyleSheet.create((theme) => ({\n row: { flexDirection: 'row', gap: theme.spacing(2) },\n fileGap: { marginTop: theme.spacing(1) },\n mono: {\n fontFamily: fontOf(theme.fontFamily.mono),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeightRelaxed.sm,\n },\n content: { flex: 1, minWidth: 0 },\n marker: { color: theme.colors.blue },\n faint: { color: theme.colors.faint },\n dim: { color: theme.colors.dim },\n text: { color: theme.colors.text },\n hi: { color: theme.colors.hi },\n}));\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { fontOf } from "../lib/fonts.mjs";
2
2
  import { useInsideSheet } from "../lib/insideSheet.mjs";
3
- import { withAlpha } from "@agimon-ai/po-web-components/nativeTheme";
4
3
  import { StyleSheet, useUnistyles, withUnistyles } from "react-native-unistyles";
4
+ import { withAlpha } from "@agimon-ai/po-web-components/nativeTheme";
5
5
  import { useState } from "react";
6
6
  import { jsx } from "react/jsx-runtime";
7
7
  import { BottomSheetTextInput } from "@gorhom/bottom-sheet";
@@ -1,6 +1,6 @@
1
1
  import { fontOf } from "../lib/fonts.mjs";
2
- import { StyleSheet } from "react-native-unistyles";
3
2
  import { Text } from "react-native-unistyles/components/native/Text";
3
+ import { StyleSheet } from "react-native-unistyles";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
 
6
6
  //#region src/components/Label.tsx
@@ -1,9 +1,9 @@
1
1
  import { fontOf } from "../lib/fonts.mjs";
2
2
  import { ChevronRightIcon } from "../icons/icons.mjs";
3
3
  import { View } from "react-native-unistyles/components/native/View";
4
+ import { Text } from "react-native-unistyles/components/native/Text";
4
5
  import { StyleSheet } from "react-native-unistyles";
5
6
  import { Platform } from "react-native";
6
- import { Text } from "react-native-unistyles/components/native/Text";
7
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
8
  import { Pressable } from "react-native-unistyles/components/native/Pressable";
9
9
  import { FlashList } from "@shopify/flash-list";
@@ -5,11 +5,18 @@
5
5
  * contract as the web Markdown's handler.
6
6
  */
7
7
  export type FileLinkHandler = (text: string, explicit?: boolean) => (() => void) | undefined;
8
+ /** Loads a relative image path from the host; the same contract as the web Markdown's `loadImage`. */
9
+ export type MarkdownImageLoader = (path: string) => Promise<{
10
+ url: string;
11
+ dispose(): void;
12
+ }>;
8
13
  export interface MarkdownProps {
9
14
  text: string;
10
15
  onFileLink?: FileLinkHandler;
16
+ /** Loads relative image paths; without it they show as unavailable. */
17
+ loadImage?: MarkdownImageLoader;
11
18
  }
12
19
  /** Markdown in Po's shared presentation, parsed by the package's own small parser. */
13
- export declare function Markdown({ text, onFileLink }: MarkdownProps): import("react").JSX.Element;
20
+ export declare function Markdown({ text, onFileLink, loadImage }: MarkdownProps): import("react").JSX.Element;
14
21
  //#endregion
15
22
  //# sourceMappingURL=Markdown.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Markdown.d.mts","names":[],"sources":["../../src/components/Markdown.tsx"],"mappings":";;;;;;YAqBY,mBAAmB,cAAc;iBAgH5B;EACf;EACA,aAAa;;;wBAIC,WAAW,MAAM,cAAc,gCAAa,IAAA"}
1
+ {"version":3,"file":"Markdown.d.mts","names":[],"sources":["../../src/components/Markdown.tsx"],"mappings":";;;;;;YAoBY,mBAAmB,cAAc;;YAMjC,uBAAuB,iBAAiB;EAAU;EAAa;;iBA0O1D;EACf;EACA,aAAa;;EAEb,YAAY;;;wBAIE,WAAW,MAAM,YAAY,aAAa,gCAAa,IAAA"}