@agimon-ai/po-native-components 0.0.0-alpha.33 → 0.0.0-alpha.35

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.
@@ -1 +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"}
1
+ {"version":3,"file":"HashlineLines.d.mts","names":[],"sources":["../../src/components/HashlineLines.tsx"],"mappings":";;;iBA0BiB,2BAA2B,KAAK;EAC/C,gBAAgB;;EAEhB;;EAEA;;wBAwCc,gBAAgB,OAAO,QAAQ,MAAM,UAAU,SAAS,qCAAkB,IAAA"}
@@ -16,6 +16,7 @@ function useHashlineSpans(lines, path) {
16
16
  const [done, setDone] = useState();
17
17
  useEffect(() => {
18
18
  let live = true;
19
+ const controller = new AbortController();
19
20
  const next = /* @__PURE__ */ new Map();
20
21
  const work = groups.map(async (group) => {
21
22
  const grammar = detectGrammar({
@@ -23,7 +24,7 @@ function useHashlineSpans(lines, path) {
23
24
  text: group.text
24
25
  });
25
26
  if (grammar === void 0) return;
26
- const highlighted = await highlightToLines(group.text, grammar);
27
+ const highlighted = await highlightToLines(group.text, grammar, controller.signal);
27
28
  if (highlighted === void 0) return;
28
29
  for (const [offset, index] of group.indices.entries()) {
29
30
  const line = highlighted[offset];
@@ -38,6 +39,7 @@ function useHashlineSpans(lines, path) {
38
39
  });
39
40
  return () => {
40
41
  live = false;
42
+ controller.abort();
41
43
  };
42
44
  }, [key]);
43
45
  return done?.key === key ? done.spans : NO_SPANS;
@@ -1 +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
+ {"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","controller","AbortController","next","work","map","group","grammar","text","undefined","highlighted","signal","offset","index","indices","entries","line","set","Promise","allSettled","then","size","abort","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 controller = new AbortController();\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, controller.signal);\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 controller.abort();\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 +1 @@
1
- {"version":3,"file":"List.d.mts","names":[],"sources":["../../src/components/List.tsx"],"mappings":";;;iBAoBiB,UAAU;EACzB,eAAe;EACf,aAAa,MAAM,GAAG,kBAAkB;;EAExC,eAAe,MAAM,GAAG;;;;;EAKxB;;EAEA;;EAEA,QAAQ;EACR,SAAS;EACT,SAAS;EACT;EACA;;EAEA,QAAQ,UAAU;;EAElB;;wBAOc,KAAK,KACnB,MACA,YACA,cACA,eACA,YACA,OACA,QACA,QACA,cACA,oBACA,OACA,kBACC,UAAU,qBAAE,IAAA;iBAuBE;EACf,OAAO;;EAEP,cAAc;;EAEd,UAAU;;EAEV,WAAW;;EAEX;EACA;EACA;;EAEA;EACA;EACA,QAAQ,UAAU;;;wBAIJ,UACd,OACA,aACA,SACA,UACA,UACA,UACA,SACA,YACA,oBACA,SACC,+BAAY,IAAA"}
1
+ {"version":3,"file":"List.d.mts","names":[],"sources":["../../src/components/List.tsx"],"mappings":";;;iBAoBiB,UAAU;EACzB,eAAe;EACf,aAAa,MAAM,GAAG,kBAAkB;;EAExC,eAAe,MAAM,GAAG;;;;;EAKxB;;EAEA;;EAEA,QAAQ;EACR,SAAS;EACT,SAAS;EACT;EACA;;EAEA,QAAQ,UAAU;;EAElB;;wBAOc,KAAK,KACnB,MACA,YACA,cACA,eACA,YACA,OACA,QACA,QACA,cACA,oBACA,OACA,kBACC,UAAU,qBAAE,IAAA;iBAwBE;EACf,OAAO;;EAEP,cAAc;;EAEd,UAAU;;EAEV,WAAW;;EAEX;EACA;EACA;;EAEA;EACA;EACA,QAAQ,UAAU;;;wBAIJ,UACd,OACA,aACA,SACA,UACA,UACA,UACA,SACA,YACA,oBACA,SACC,+BAAY,IAAA"}
@@ -4,6 +4,7 @@ import { View } from "react-native-unistyles/components/native/View";
4
4
  import { Text } from "react-native-unistyles/components/native/Text";
5
5
  import { StyleSheet } from "react-native-unistyles";
6
6
  import { Platform } from "react-native";
7
+ import { useCallback } from "react";
7
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
9
  import { Pressable } from "react-native-unistyles/components/native/Pressable";
9
10
  import { FlashList } from "@shopify/flash-list";
@@ -17,12 +18,13 @@ function Separator() {
17
18
  return /* @__PURE__ */ jsx(View, { style: styles.separator });
18
19
  }
19
20
  function List({ data, renderItem, keyExtractor, startAtBottom = false, separators = true, empty, header, footer, onEndReached, accessibilityLabel, style, contentPadding }) {
21
+ const renderRow = useCallback(({ item, index }) => renderItem(item, index), [renderItem]);
20
22
  return /* @__PURE__ */ jsx(View, {
21
23
  style: [styles.frame, style],
22
24
  accessibilityLabel,
23
25
  children: /* @__PURE__ */ jsx(FlashList, {
24
26
  data,
25
- renderItem: ({ item, index }) => renderItem(item, index),
27
+ renderItem: renderRow,
26
28
  keyExtractor,
27
29
  ItemSeparatorComponent: separators ? Separator : null,
28
30
  ListEmptyComponent: empty === void 0 ? null : /* @__PURE__ */ jsx(Fragment, { children: empty }),
@@ -1 +1 @@
1
- {"version":3,"file":"List.mjs","names":["FlashList","ReactElement","ReactNode","Platform","StyleProp","ViewStyle","StyleSheet","ChevronRightIcon","fontOf","MIN_ROW_HEIGHT","DISABLED_OPACITY","PIN_THRESHOLD","ListProps","data","T","renderItem","item","index","keyExtractor","startAtBottom","separators","empty","header","footer","onEndReached","accessibilityLabel","style","contentPadding","Separator","styles","separator","List","frame","undefined","padding","startRenderingFromBottom","autoscrollToBottomThreshold","ListRowProps","title","description","leading","trailing","selected","disabled","onPress","disclosure","ListRow","chevron","OS","content","text","titleSelected","row","pressed","create","theme","flex","minHeight","height","hairlineWidth","marginLeft","spacing","backgroundColor","colors","uni__dependencies","flexDirection","alignItems","gap","paddingHorizontal","paddingVertical","panel","opacity","minWidth","color","hi","fontFamily","sans","fontSize","base","lineHeight","fontWeight","medium","green","semibold","faint","sm"],"sources":["../../src/components/List.tsx"],"sourcesContent":["import { FlashList } from '@shopify/flash-list';\nimport type { ReactElement, ReactNode } from 'react';\nimport { Platform, Pressable, type StyleProp, Text, View, type ViewStyle } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { ChevronRightIcon } from '../icons/icons';\nimport { fontOf } from '../lib/fonts';\n\n/*\n * A virtualised list on FlashList: sessions, devices, a transcript. Rows\n * recycle, separators are hairlines, and a chat list can start at the bottom\n * and stay pinned there as rows arrive. The public props are this package's\n * own, so FlashList's types never reach a caller.\n */\n\nconst MIN_ROW_HEIGHT = 44;\nconst DISABLED_OPACITY = 0.4;\n/** How close to the bottom a reader must be for a new row to keep the list pinned, as a fraction of the visible height. */\nconst PIN_THRESHOLD = 0.1;\n\nexport interface ListProps<T> {\n data: readonly T[];\n renderItem: (item: T, index: number) => ReactElement | null;\n /** A stable key per row; FlashList recycles views by it. */\n keyExtractor: (item: T, index: number) => string;\n /**\n * A chat list: render from the bottom and keep the newest row in view as\n * rows arrive, while the reader is near the bottom.\n */\n startAtBottom?: boolean;\n /** Hairline separators between rows. On by default. */\n separators?: boolean;\n /** Shown when `data` is empty. */\n empty?: ReactNode;\n header?: ReactNode;\n footer?: ReactNode;\n onEndReached?: () => void;\n accessibilityLabel?: string;\n /** The list's frame; give it a height or a flex parent, since a virtualised list needs bounds. */\n style?: StyleProp<ViewStyle>;\n /** Padding around the rows. */\n contentPadding?: number;\n}\n\nfunction Separator() {\n return <View style={styles.separator} />;\n}\n\nexport function List<T>({\n data,\n renderItem,\n keyExtractor,\n startAtBottom = false,\n separators = true,\n empty,\n header,\n footer,\n onEndReached,\n accessibilityLabel,\n style,\n contentPadding,\n}: ListProps<T>) {\n return (\n <View style={[styles.frame, style]} accessibilityLabel={accessibilityLabel}>\n <FlashList\n data={data}\n renderItem={({ item, index }) => renderItem(item, index)}\n keyExtractor={keyExtractor}\n ItemSeparatorComponent={separators ? Separator : null}\n ListEmptyComponent={empty === undefined ? null : <>{empty}</>}\n ListHeaderComponent={header === undefined ? null : <>{header}</>}\n ListFooterComponent={footer === undefined ? null : <>{footer}</>}\n onEndReached={onEndReached}\n // Lets UIKit inset the rows under a translucent or large-title header instead of drawing beneath it.\n contentInsetAdjustmentBehavior=\"automatic\"\n contentContainerStyle={contentPadding === undefined ? undefined : { padding: contentPadding }}\n maintainVisibleContentPosition={\n startAtBottom ? { startRenderingFromBottom: true, autoscrollToBottomThreshold: PIN_THRESHOLD } : undefined\n }\n />\n </View>\n );\n}\n\nexport interface ListRowProps {\n title: ReactNode;\n /** A second, quieter line under the title. */\n description?: ReactNode;\n /** Before the text: a Dot, an icon. */\n leading?: ReactNode;\n /** After the text: a Badge, a ghost button, a count. */\n trailing?: ReactNode;\n /** Selected rows sit on a green tint. */\n selected?: boolean;\n disabled?: boolean;\n onPress?: () => void;\n /** The iOS disclosure chevron. Defaults to on for a pressable row on iOS, where rows that navigate wear one. */\n disclosure?: boolean;\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/** One list row: 44pt tall, optional leading and trailing slots, pressable when it navigates. */\nexport function ListRow({\n title,\n description,\n leading,\n trailing,\n selected = false,\n disabled = false,\n onPress,\n disclosure,\n accessibilityLabel,\n style,\n}: ListRowProps) {\n const chevron = disclosure ?? (onPress !== undefined && Platform.OS === 'ios');\n const content = (\n <>\n {leading}\n <View style={styles.text}>\n {typeof title === 'string' ? (\n <Text numberOfLines={1} style={[styles.title, selected && styles.titleSelected]}>\n {title}\n </Text>\n ) : (\n title\n )}\n {typeof description === 'string' ? (\n <Text numberOfLines={2} style={styles.description}>\n {description}\n </Text>\n ) : (\n description\n )}\n </View>\n {trailing}\n {chevron ? <ChevronRightIcon size={14} color=\"faint\" /> : null}\n </>\n );\n if (onPress === undefined) {\n return (\n <View\n accessibilityLabel={accessibilityLabel}\n accessibilityState={{ selected, disabled }}\n style={[styles.row, selected && styles.selected, disabled && styles.disabled, style]}\n >\n {content}\n </View>\n );\n }\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={accessibilityLabel}\n accessibilityState={{ selected, disabled }}\n disabled={disabled}\n onPress={onPress}\n style={({ pressed }) => [\n styles.row,\n selected && styles.selected,\n pressed && styles.pressed,\n disabled && styles.disabled,\n style,\n ]}\n >\n {content}\n </Pressable>\n );\n}\n\nconst styles = StyleSheet.create((theme) => ({\n frame: { flex: 1, minHeight: 0 },\n separator: {\n height: StyleSheet.hairlineWidth,\n marginLeft: theme.spacing(4),\n backgroundColor: theme.colors['border-soft'],\n },\n row: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.spacing(3),\n minHeight: MIN_ROW_HEIGHT,\n paddingHorizontal: theme.spacing(4),\n paddingVertical: theme.spacing(2),\n },\n selected: { backgroundColor: theme.colors['tint-green'] },\n pressed: { backgroundColor: theme.colors.panel },\n disabled: { opacity: DISABLED_OPACITY },\n text: { flex: 1, minWidth: 0, gap: theme.spacing(0.5) },\n title: {\n color: theme.colors.hi,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n fontWeight: theme.fontWeight.medium,\n },\n titleSelected: { color: theme.colors.green, fontWeight: theme.fontWeight.semibold },\n description: {\n color: theme.colors.faint,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n}));\n"],"mappings":"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"}
1
+ {"version":3,"file":"List.mjs","names":["FlashList","ReactElement","ReactNode","useCallback","Platform","StyleProp","ViewStyle","StyleSheet","ChevronRightIcon","fontOf","MIN_ROW_HEIGHT","DISABLED_OPACITY","PIN_THRESHOLD","ListProps","data","T","renderItem","item","index","keyExtractor","startAtBottom","separators","empty","header","footer","onEndReached","accessibilityLabel","style","contentPadding","Separator","styles","separator","List","renderRow","frame","undefined","padding","startRenderingFromBottom","autoscrollToBottomThreshold","ListRowProps","title","description","leading","trailing","selected","disabled","onPress","disclosure","ListRow","chevron","OS","content","text","titleSelected","row","pressed","create","theme","flex","minHeight","height","hairlineWidth","marginLeft","spacing","backgroundColor","colors","uni__dependencies","flexDirection","alignItems","gap","paddingHorizontal","paddingVertical","panel","opacity","minWidth","color","hi","fontFamily","sans","fontSize","base","lineHeight","fontWeight","medium","green","semibold","faint","sm"],"sources":["../../src/components/List.tsx"],"sourcesContent":["import { FlashList } from '@shopify/flash-list';\nimport { type ReactElement, type ReactNode, useCallback } from 'react';\nimport { Platform, Pressable, type StyleProp, Text, View, type ViewStyle } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { ChevronRightIcon } from '../icons/icons';\nimport { fontOf } from '../lib/fonts';\n\n/*\n * A virtualised list on FlashList: sessions, devices, a transcript. Rows\n * recycle, separators are hairlines, and a chat list can start at the bottom\n * and stay pinned there as rows arrive. The public props are this package's\n * own, so FlashList's types never reach a caller.\n */\n\nconst MIN_ROW_HEIGHT = 44;\nconst DISABLED_OPACITY = 0.4;\n/** How close to the bottom a reader must be for a new row to keep the list pinned, as a fraction of the visible height. */\nconst PIN_THRESHOLD = 0.1;\n\nexport interface ListProps<T> {\n data: readonly T[];\n renderItem: (item: T, index: number) => ReactElement | null;\n /** A stable key per row; FlashList recycles views by it. */\n keyExtractor: (item: T, index: number) => string;\n /**\n * A chat list: render from the bottom and keep the newest row in view as\n * rows arrive, while the reader is near the bottom.\n */\n startAtBottom?: boolean;\n /** Hairline separators between rows. On by default. */\n separators?: boolean;\n /** Shown when `data` is empty. */\n empty?: ReactNode;\n header?: ReactNode;\n footer?: ReactNode;\n onEndReached?: () => void;\n accessibilityLabel?: string;\n /** The list's frame; give it a height or a flex parent, since a virtualised list needs bounds. */\n style?: StyleProp<ViewStyle>;\n /** Padding around the rows. */\n contentPadding?: number;\n}\n\nfunction Separator() {\n return <View style={styles.separator} />;\n}\n\nexport function List<T>({\n data,\n renderItem,\n keyExtractor,\n startAtBottom = false,\n separators = true,\n empty,\n header,\n footer,\n onEndReached,\n accessibilityLabel,\n style,\n contentPadding,\n}: ListProps<T>) {\n const renderRow = useCallback(({ item, index }: { item: T; index: number }) => renderItem(item, index), [renderItem]);\n return (\n <View style={[styles.frame, style]} accessibilityLabel={accessibilityLabel}>\n <FlashList\n data={data}\n renderItem={renderRow}\n keyExtractor={keyExtractor}\n ItemSeparatorComponent={separators ? Separator : null}\n ListEmptyComponent={empty === undefined ? null : <>{empty}</>}\n ListHeaderComponent={header === undefined ? null : <>{header}</>}\n ListFooterComponent={footer === undefined ? null : <>{footer}</>}\n onEndReached={onEndReached}\n // Lets UIKit inset the rows under a translucent or large-title header instead of drawing beneath it.\n contentInsetAdjustmentBehavior=\"automatic\"\n contentContainerStyle={contentPadding === undefined ? undefined : { padding: contentPadding }}\n maintainVisibleContentPosition={\n startAtBottom ? { startRenderingFromBottom: true, autoscrollToBottomThreshold: PIN_THRESHOLD } : undefined\n }\n />\n </View>\n );\n}\n\nexport interface ListRowProps {\n title: ReactNode;\n /** A second, quieter line under the title. */\n description?: ReactNode;\n /** Before the text: a Dot, an icon. */\n leading?: ReactNode;\n /** After the text: a Badge, a ghost button, a count. */\n trailing?: ReactNode;\n /** Selected rows sit on a green tint. */\n selected?: boolean;\n disabled?: boolean;\n onPress?: () => void;\n /** The iOS disclosure chevron. Defaults to on for a pressable row on iOS, where rows that navigate wear one. */\n disclosure?: boolean;\n accessibilityLabel?: string;\n style?: StyleProp<ViewStyle>;\n}\n\n/** One list row: 44pt tall, optional leading and trailing slots, pressable when it navigates. */\nexport function ListRow({\n title,\n description,\n leading,\n trailing,\n selected = false,\n disabled = false,\n onPress,\n disclosure,\n accessibilityLabel,\n style,\n}: ListRowProps) {\n const chevron = disclosure ?? (onPress !== undefined && Platform.OS === 'ios');\n const content = (\n <>\n {leading}\n <View style={styles.text}>\n {typeof title === 'string' ? (\n <Text numberOfLines={1} style={[styles.title, selected && styles.titleSelected]}>\n {title}\n </Text>\n ) : (\n title\n )}\n {typeof description === 'string' ? (\n <Text numberOfLines={2} style={styles.description}>\n {description}\n </Text>\n ) : (\n description\n )}\n </View>\n {trailing}\n {chevron ? <ChevronRightIcon size={14} color=\"faint\" /> : null}\n </>\n );\n if (onPress === undefined) {\n return (\n <View\n accessibilityLabel={accessibilityLabel}\n accessibilityState={{ selected, disabled }}\n style={[styles.row, selected && styles.selected, disabled && styles.disabled, style]}\n >\n {content}\n </View>\n );\n }\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={accessibilityLabel}\n accessibilityState={{ selected, disabled }}\n disabled={disabled}\n onPress={onPress}\n style={({ pressed }) => [\n styles.row,\n selected && styles.selected,\n pressed && styles.pressed,\n disabled && styles.disabled,\n style,\n ]}\n >\n {content}\n </Pressable>\n );\n}\n\nconst styles = StyleSheet.create((theme) => ({\n frame: { flex: 1, minHeight: 0 },\n separator: {\n height: StyleSheet.hairlineWidth,\n marginLeft: theme.spacing(4),\n backgroundColor: theme.colors['border-soft'],\n },\n row: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.spacing(3),\n minHeight: MIN_ROW_HEIGHT,\n paddingHorizontal: theme.spacing(4),\n paddingVertical: theme.spacing(2),\n },\n selected: { backgroundColor: theme.colors['tint-green'] },\n pressed: { backgroundColor: theme.colors.panel },\n disabled: { opacity: DISABLED_OPACITY },\n text: { flex: 1, minWidth: 0, gap: theme.spacing(0.5) },\n title: {\n color: theme.colors.hi,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n fontWeight: theme.fontWeight.medium,\n },\n titleSelected: { color: theme.colors.green, fontWeight: theme.fontWeight.semibold },\n description: {\n color: theme.colors.faint,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n}));\n"],"mappings":"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"}
@@ -17,6 +17,6 @@ export interface MarkdownProps {
17
17
  loadImage?: MarkdownImageLoader;
18
18
  }
19
19
  /** Markdown in Po's shared presentation, parsed by the package's own small parser. */
20
- export declare function Markdown({ text, onFileLink, loadImage }: MarkdownProps): import("react").JSX.Element;
20
+ export declare const Markdown: import("react").MemoExoticComponent<({ text, onFileLink, loadImage }: MarkdownProps) => import("react").JSX.Element>;
21
21
  //#endregion
22
22
  //# sourceMappingURL=Markdown.d.mts.map
@@ -1 +1 @@
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"}
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;;;qBAID,0BAAQ,uBAAA,MAAA,YAAA,aAA2D,kCAAa,IAAA"}
@@ -5,7 +5,7 @@ import { View } from "react-native-unistyles/components/native/View";
5
5
  import { Text } from "react-native-unistyles/components/native/Text";
6
6
  import { StyleSheet } from "react-native-unistyles";
7
7
  import { Linking } from "react-native";
8
- import { createContext, useContext, useEffect, useMemo, useState } from "react";
8
+ import { createContext, memo, useContext, useEffect, useMemo, useState } from "react";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { ScrollView } from "react-native-unistyles/components/native/ScrollView";
11
11
  import { Image } from "react-native-unistyles/components/native/Image";
@@ -244,7 +244,7 @@ function Block({ block, first, quoted = false }) {
244
244
  }
245
245
  }
246
246
  /** Markdown in Po's shared presentation, parsed by the package's own small parser. */
247
- function Markdown({ text, onFileLink, loadImage }) {
247
+ const Markdown = memo(function Markdown({ text, onFileLink, loadImage }) {
248
248
  const blocks = useMemo(() => parseMarkdown(text), [text]);
249
249
  return /* @__PURE__ */ jsx(FileLinkContext.Provider, {
250
250
  value: onFileLink,
@@ -259,7 +259,7 @@ function Markdown({ text, onFileLink, loadImage }) {
259
259
  })
260
260
  })
261
261
  });
262
- }
262
+ });
263
263
  /**
264
264
  * Headings step up from the prose and never shrink below it; h4 to h6 keep
265
265
  * the prose size and lean on weight, fading from `hi` to `dim`.
@@ -1 +1 @@
1
- {"version":3,"file":"Markdown.mjs","names":["createContext","ReactNode","useContext","useEffect","useMemo","useState","Linking","StyleSheet","fontOf","MarkdownBlock","MarkdownHeadingLevel","MarkdownInline","parseMarkdown","CodeBlock","FileLinkHandler","text","explicit","FileLinkContext","undefined","MarkdownImageLoader","path","Promise","url","dispose","ImageContext","URL_OR_ANCHOR","DIRECT_IMAGE","PLACEHOLDER_RATIO","decoded","href","decodeURIComponent","InlineCode","onFileLink","open","includes","styles","code","fileLink","InlineLink","children","isFile","test","openURL","catch","link","MarkdownImage","src","alt","loader","split","loaded","setLoaded","failed","ratio","setRatio","active","asset","Awaited","ReturnType","resolve","then","Error","result","current","uri","waiting","prose","imageState","nativeEvent","fit","width","height","size","source","Image","getSize","image","aspectRatio","renderInline","nodes","map","node","index","type","strong","em","MarkdownTable","header","rows","table","cell","column","headCell","row","Block","block","first","quoted","heading","level","firstHeading","parts","run","flush","length","push","root","list","items","item","marker","ordered","start","itemText","language","rule","quote","child","MarkdownProps","loadImage","Markdown","blocks","HEADING_STEP","const","HEADING_SPACE","HEADING_INK","create","theme","minWidth","gap","spacing","uni__dependencies","color","colors","fontFamily","sans","fontSize","base","lineHeight","marginTop","letterSpacing","fontWeight","semibold","hi","fontStyle","backgroundColor","deep","green","mono","textDecorationLine","blue","flexDirection","paddingLeft","dim","flexShrink","borderLeftWidth","borderLeftColor","border","borderTopWidth","borderColor","paddingHorizontal","paddingVertical","borderRightWidth","borderBottomWidth","maxHeight","borderRadius","md","borderWidth"],"sources":["../../src/components/Markdown.tsx"],"sourcesContent":["import { createContext, type ReactNode, useContext, useEffect, useMemo, useState } from 'react';\nimport { Image, Linking, ScrollView, Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { fontOf } from '../lib/fonts';\nimport { type MarkdownBlock, type MarkdownHeadingLevel, type MarkdownInline, parseMarkdown } from '../lib/markdown';\nimport { CodeBlock } from './CodeBlock';\n\n/*\n * Markdown in the web component's element styles, from the package's own\n * parser: headings, paragraphs, lists, code spans and fences, bold, italic,\n * links, images, blockquotes and tables. Web links open with Linking; a file\n * path goes to the host. Raw HTML blocks are left out, as on the web.\n */\n\n/**\n * What a piece of inline code or a relative link opens, when the host knows.\n * Returning undefined leaves it as plain code or an ordinary link. The same\n * contract as the web Markdown's handler.\n */\nexport type FileLinkHandler = (text: string, explicit?: boolean) => (() => void) | undefined;\n\n/** Passed by context so the inline renderers stay module functions. */\nconst FileLinkContext = createContext<FileLinkHandler | undefined>(undefined);\n\n/** Loads a relative image path from the host; the same contract as the web Markdown's `loadImage`. */\nexport type MarkdownImageLoader = (path: string) => Promise<{ url: string; dispose(): void }>;\n\nconst ImageContext = createContext<MarkdownImageLoader | undefined>(undefined);\n\nconst URL_OR_ANCHOR = /^(?:[a-z][a-z\\d+.-]*:|\\/\\/|#|\\?)/i;\n/** Sources an Image may load as written; any other scheme stays its alt text. */\nconst DIRECT_IMAGE = /^(?:https?:|data:image\\/)/i;\n/** Until the image reports its size, it holds a landscape box rather than collapsing. */\nconst PLACEHOLDER_RATIO = 16 / 9;\n\nfunction decoded(href: string): string | undefined {\n try {\n return decodeURIComponent(href);\n } catch {\n return undefined;\n }\n}\n\nfunction InlineCode({ text }: { text: string }) {\n const onFileLink = useContext(FileLinkContext);\n const open = text.includes('\\n') ? undefined : onFileLink?.(text);\n if (open !== undefined) {\n return (\n <Text\n accessibilityRole=\"link\"\n accessibilityHint={`open ${text}`}\n onPress={open}\n style={[styles.code, styles.fileLink]}\n >\n {text}\n </Text>\n );\n }\n return <Text style={styles.code}>{text}</Text>;\n}\n\nfunction InlineLink({ href, children }: { href: string; children: ReactNode }) {\n const onFileLink = useContext(FileLinkContext);\n const path = decoded(href);\n const isFile = path !== undefined && path !== '' && !URL_OR_ANCHOR.test(path);\n const open = isFile ? onFileLink?.(path, true) : undefined;\n return (\n <Text\n accessibilityRole=\"link\"\n onPress={() => {\n if (open !== undefined) open();\n else if (!isFile) Linking.openURL(href).catch(() => undefined);\n }}\n style={styles.link}\n >\n {children}\n </Text>\n );\n}\n\n/** One image: a file path through the host's loader, or a web or data address as written. */\nfunction MarkdownImage({ src, alt }: { src: string; alt: string }) {\n const loader = useContext(ImageContext);\n // An invalid encoded filename is shown as unavailable, never fetched.\n const path = URL_OR_ANCHOR.test(src) ? undefined : (decoded(src.split(/[?#]/u, 1)[0]!) ?? '');\n const [loaded, setLoaded] = useState<{ path: string; url?: string; failed?: boolean }>();\n const [ratio, setRatio] = useState(PLACEHOLDER_RATIO);\n useEffect(() => {\n if (loader === undefined || path === undefined) return;\n let active = true;\n let asset: Awaited<ReturnType<MarkdownImageLoader>> | undefined;\n void Promise.resolve()\n .then(() => {\n if (path === '') throw new Error('Invalid image path.');\n return loader(path);\n })\n .then((result) => {\n if (!active) return result.dispose();\n asset = result;\n setLoaded({ path, url: result.url });\n })\n .catch(() => {\n // The failure is shown in place, as the web image does.\n if (active) setLoaded({ path, failed: true });\n });\n return () => {\n active = false;\n asset?.dispose();\n };\n }, [path, loader]);\n const current = loaded?.path === path ? loaded : undefined;\n const uri = path === undefined ? (DIRECT_IMAGE.test(src) ? src : undefined) : current?.url;\n if (uri === undefined || current?.failed) {\n const waiting = path !== undefined && loader !== undefined && current === undefined;\n return (\n <Text style={[styles.prose, styles.imageState]} accessibilityRole={waiting ? 'text' : 'alert'}>\n {alt || path || 'Image'} ({waiting ? 'loading\\u2026' : 'unavailable'})\n </Text>\n );\n }\n return (\n <Image\n source={{ uri }}\n accessibilityLabel={alt || 'Image'}\n accessibilityIgnoresInvertColors\n resizeMode=\"contain\"\n onLoad={({ nativeEvent }) => {\n const fit = (width: number, height: number) => {\n if (width > 0 && height > 0) setRatio(width / height);\n };\n // iOS and Android report the size with the event; react-native-web does not, so it asks.\n const size = (nativeEvent as { source?: { width: number; height: number } } | undefined)?.source;\n if (size !== undefined) fit(size.width, size.height);\n else Image.getSize(uri, fit, () => undefined);\n }}\n onError={() => setLoaded({ path: path ?? src, failed: true })}\n style={[styles.image, { aspectRatio: ratio }]}\n />\n );\n}\nfunction renderInline(nodes: readonly MarkdownInline[]): ReactNode[] {\n return nodes.map((node, index) => {\n switch (node.type) {\n case 'text':\n return node.text;\n case 'code':\n return <InlineCode key={index} text={node.text} />;\n case 'strong':\n return (\n <Text key={index} style={styles.strong}>\n {renderInline(node.children)}\n </Text>\n );\n case 'em':\n return (\n <Text key={index} style={styles.em}>\n {renderInline(node.children)}\n </Text>\n );\n case 'link':\n return (\n <InlineLink key={index} href={node.href}>\n {renderInline(node.children)}\n </InlineLink>\n );\n case 'image':\n // Inside a heading or list item an image keeps to its alt text; paragraphs draw it.\n return node.alt;\n }\n });\n}\n\n/**\n * A table read column by column: every cell keeps to one line inside the sideways scroll, so each\n * column takes its widest cell and the rows line up without a table layout.\n */\nfunction MarkdownTable({ header, rows }: { header: MarkdownInline[][]; rows: MarkdownInline[][][] }) {\n return (\n <ScrollView horizontal showsHorizontalScrollIndicator={false}>\n <View style={styles.table}>\n {header.map((cell, column) => (\n <View key={column}>\n <Text style={[styles.prose, styles.cell, styles.headCell]}>{renderInline(cell)}</Text>\n {rows.map((row, index) => (\n <Text key={index} style={[styles.prose, styles.cell]}>\n {renderInline(row[column] ?? [])}\n </Text>\n ))}\n </View>\n ))}\n </View>\n </ScrollView>\n );\n}\n\nfunction Block({ block, first, quoted = false }: { block: MarkdownBlock; first: boolean; quoted?: boolean }) {\n const prose = [styles.prose, quoted && styles.quoted];\n switch (block.type) {\n case 'heading':\n return (\n <Text\n accessibilityRole=\"header\"\n style={[...prose, styles.heading(block.level), first && styles.firstHeading, quoted && styles.quoted]}\n >\n {renderInline(block.children)}\n </Text>\n );\n case 'paragraph': {\n // An image breaks the paragraph's text into runs, since an Image cannot sit inside Text.\n const parts: ReactNode[] = [];\n let run: MarkdownInline[] = [];\n const flush = () => {\n if (run.length > 0)\n parts.push(\n <Text key={parts.length} style={prose}>\n {renderInline(run)}\n </Text>,\n );\n run = [];\n };\n for (const node of block.children) {\n if (node.type !== 'image') {\n run.push(node);\n continue;\n }\n flush();\n parts.push(<MarkdownImage key={parts.length} src={node.src} alt={node.alt} />);\n }\n flush();\n return parts.length === 1 ? parts[0] : <View style={styles.root}>{parts}</View>;\n }\n case 'list':\n return (\n <View accessibilityRole=\"list\" style={styles.list}>\n {block.items.map((item, index) => (\n <View key={index} style={styles.item}>\n <Text style={[...prose, styles.marker]}>{block.ordered ? `${block.start + index}.` : '\\u2022'}</Text>\n <Text style={[...prose, styles.itemText]}>{renderInline(item)}</Text>\n </View>\n ))}\n </View>\n );\n case 'code':\n return <CodeBlock text={block.text} language={block.language} />;\n case 'rule':\n return <View accessible={false} style={styles.rule} />;\n case 'quote':\n return (\n <View style={[styles.root, styles.quote]}>\n {block.children.map((child, index) => (\n <Block key={index} block={child} first={index === 0} quoted />\n ))}\n </View>\n );\n case 'table':\n return <MarkdownTable header={block.header} rows={block.rows} />;\n }\n}\n\nexport interface MarkdownProps {\n text: string;\n onFileLink?: FileLinkHandler;\n /** Loads relative image paths; without it they show as unavailable. */\n loadImage?: MarkdownImageLoader;\n}\n\n/** Markdown in Po's shared presentation, parsed by the package's own small parser. */\nexport function Markdown({ text, onFileLink, loadImage }: MarkdownProps) {\n const blocks = useMemo(() => parseMarkdown(text), [text]);\n return (\n <FileLinkContext.Provider value={onFileLink}>\n <ImageContext.Provider value={loadImage}>\n <View style={styles.root}>\n {blocks.map((block, index) => (\n <Block key={index} block={block} first={index === 0} />\n ))}\n </View>\n </ImageContext.Provider>\n </FileLinkContext.Provider>\n );\n}\n\n/**\n * Headings step up from the prose and never shrink below it; h4 to h6 keep\n * the prose size and lean on weight, fading from `hi` to `dim`.\n */\nconst HEADING_STEP = { 1: '2xl', 2: 'xl', 3: 'lg', 4: 'base', 5: 'base', 6: 'base' } as const;\nconst HEADING_SPACE = { 1: 2, 2: 2, 3: 1, 4: 0, 5: 0, 6: 0 } as const;\nconst HEADING_INK = { 1: 'hi', 2: 'hi', 3: 'hi', 4: 'hi', 5: 'text', 6: 'dim' } as const;\n\nconst styles = StyleSheet.create((theme) => ({\n root: { minWidth: 0, gap: theme.spacing(2) },\n prose: {\n color: theme.colors.text,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n },\n heading: (level: MarkdownHeadingLevel) => ({\n marginTop: theme.spacing(HEADING_SPACE[level]),\n fontSize: theme.fontSize[HEADING_STEP[level]],\n lineHeight: theme.lineHeight[HEADING_STEP[level]],\n letterSpacing: theme.letterSpacing[HEADING_STEP[level]],\n fontWeight: theme.fontWeight.semibold,\n color: theme.colors[HEADING_INK[level]],\n }),\n firstHeading: { marginTop: 0 },\n strong: { fontWeight: theme.fontWeight.semibold, color: theme.colors.hi },\n em: { fontStyle: 'italic' },\n code: {\n backgroundColor: theme.colors.deep,\n color: theme.colors.green,\n fontFamily: fontOf(theme.fontFamily.mono),\n },\n fileLink: { textDecorationLine: 'underline' },\n link: { color: theme.colors.blue, textDecorationLine: 'underline' },\n list: { gap: theme.spacing(0.5) },\n item: { flexDirection: 'row', gap: theme.spacing(2), paddingLeft: theme.spacing(1) },\n marker: { minWidth: theme.spacing(3), color: theme.colors.dim },\n itemText: { flexShrink: 1 },\n rule: { height: 1, backgroundColor: theme.colors['border-soft'] },\n // The web blockquote: a rule at the left and quieter text.\n quote: { borderLeftWidth: 2, borderLeftColor: theme.colors.border, paddingLeft: theme.spacing(3) },\n quoted: { color: theme.colors.dim },\n // The web table: bordered cells, the header on the deep surface.\n table: {\n flexDirection: 'row',\n borderTopWidth: 1,\n borderLeftWidth: 1,\n borderColor: theme.colors.border,\n },\n cell: {\n paddingHorizontal: theme.spacing(2),\n paddingVertical: theme.spacing(1),\n borderRightWidth: 1,\n borderBottomWidth: 1,\n borderColor: theme.colors.border,\n },\n headCell: { backgroundColor: theme.colors.deep, fontWeight: theme.fontWeight.semibold, color: theme.colors.hi },\n // The web image's frame: at most 480 tall, a soft border and rounded corners.\n image: {\n width: '100%',\n maxHeight: 480,\n borderRadius: theme.borderRadius.md,\n borderWidth: 1,\n borderColor: theme.colors.border,\n },\n imageState: { color: theme.colors.dim },\n}));\n"],"mappings":"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"}
1
+ {"version":3,"file":"Markdown.mjs","names":["createContext","memo","ReactNode","useContext","useEffect","useMemo","useState","Linking","StyleSheet","fontOf","MarkdownBlock","MarkdownHeadingLevel","MarkdownInline","parseMarkdown","CodeBlock","FileLinkHandler","text","explicit","FileLinkContext","undefined","MarkdownImageLoader","path","Promise","url","dispose","ImageContext","URL_OR_ANCHOR","DIRECT_IMAGE","PLACEHOLDER_RATIO","decoded","href","decodeURIComponent","InlineCode","onFileLink","open","includes","styles","code","fileLink","InlineLink","children","isFile","test","openURL","catch","link","MarkdownImage","src","alt","loader","split","loaded","setLoaded","failed","ratio","setRatio","active","asset","Awaited","ReturnType","resolve","then","Error","result","current","uri","waiting","prose","imageState","nativeEvent","fit","width","height","size","source","Image","getSize","image","aspectRatio","renderInline","nodes","map","node","index","type","strong","em","MarkdownTable","header","rows","table","cell","column","headCell","row","Block","block","first","quoted","heading","level","firstHeading","parts","run","flush","length","push","root","list","items","item","marker","ordered","start","itemText","language","rule","quote","child","MarkdownProps","loadImage","Markdown","blocks","HEADING_STEP","const","HEADING_SPACE","HEADING_INK","create","theme","minWidth","gap","spacing","uni__dependencies","color","colors","fontFamily","sans","fontSize","base","lineHeight","marginTop","letterSpacing","fontWeight","semibold","hi","fontStyle","backgroundColor","deep","green","mono","textDecorationLine","blue","flexDirection","paddingLeft","dim","flexShrink","borderLeftWidth","borderLeftColor","border","borderTopWidth","borderColor","paddingHorizontal","paddingVertical","borderRightWidth","borderBottomWidth","maxHeight","borderRadius","md","borderWidth"],"sources":["../../src/components/Markdown.tsx"],"sourcesContent":["import { createContext, memo, type ReactNode, useContext, useEffect, useMemo, useState } from 'react';\nimport { Image, Linking, ScrollView, Text, View } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { fontOf } from '../lib/fonts';\nimport { type MarkdownBlock, type MarkdownHeadingLevel, type MarkdownInline, parseMarkdown } from '../lib/markdown';\nimport { CodeBlock } from './CodeBlock';\n\n/*\n * Markdown in the web component's element styles, from the package's own\n * parser: headings, paragraphs, lists, code spans and fences, bold, italic,\n * links, images, blockquotes and tables. Web links open with Linking; a file\n * path goes to the host. Raw HTML blocks are left out, as on the web.\n */\n\n/**\n * What a piece of inline code or a relative link opens, when the host knows.\n * Returning undefined leaves it as plain code or an ordinary link. The same\n * contract as the web Markdown's handler.\n */\nexport type FileLinkHandler = (text: string, explicit?: boolean) => (() => void) | undefined;\n\n/** Passed by context so the inline renderers stay module functions. */\nconst FileLinkContext = createContext<FileLinkHandler | undefined>(undefined);\n\n/** Loads a relative image path from the host; the same contract as the web Markdown's `loadImage`. */\nexport type MarkdownImageLoader = (path: string) => Promise<{ url: string; dispose(): void }>;\n\nconst ImageContext = createContext<MarkdownImageLoader | undefined>(undefined);\n\nconst URL_OR_ANCHOR = /^(?:[a-z][a-z\\d+.-]*:|\\/\\/|#|\\?)/i;\n/** Sources an Image may load as written; any other scheme stays its alt text. */\nconst DIRECT_IMAGE = /^(?:https?:|data:image\\/)/i;\n/** Until the image reports its size, it holds a landscape box rather than collapsing. */\nconst PLACEHOLDER_RATIO = 16 / 9;\n\nfunction decoded(href: string): string | undefined {\n try {\n return decodeURIComponent(href);\n } catch {\n return undefined;\n }\n}\n\nfunction InlineCode({ text }: { text: string }) {\n const onFileLink = useContext(FileLinkContext);\n const open = text.includes('\\n') ? undefined : onFileLink?.(text);\n if (open !== undefined) {\n return (\n <Text\n accessibilityRole=\"link\"\n accessibilityHint={`open ${text}`}\n onPress={open}\n style={[styles.code, styles.fileLink]}\n >\n {text}\n </Text>\n );\n }\n return <Text style={styles.code}>{text}</Text>;\n}\n\nfunction InlineLink({ href, children }: { href: string; children: ReactNode }) {\n const onFileLink = useContext(FileLinkContext);\n const path = decoded(href);\n const isFile = path !== undefined && path !== '' && !URL_OR_ANCHOR.test(path);\n const open = isFile ? onFileLink?.(path, true) : undefined;\n return (\n <Text\n accessibilityRole=\"link\"\n onPress={() => {\n if (open !== undefined) open();\n else if (!isFile) Linking.openURL(href).catch(() => undefined);\n }}\n style={styles.link}\n >\n {children}\n </Text>\n );\n}\n\n/** One image: a file path through the host's loader, or a web or data address as written. */\nfunction MarkdownImage({ src, alt }: { src: string; alt: string }) {\n const loader = useContext(ImageContext);\n // An invalid encoded filename is shown as unavailable, never fetched.\n const path = URL_OR_ANCHOR.test(src) ? undefined : (decoded(src.split(/[?#]/u, 1)[0]!) ?? '');\n const [loaded, setLoaded] = useState<{ path: string; url?: string; failed?: boolean }>();\n const [ratio, setRatio] = useState(PLACEHOLDER_RATIO);\n useEffect(() => {\n if (loader === undefined || path === undefined) return;\n let active = true;\n let asset: Awaited<ReturnType<MarkdownImageLoader>> | undefined;\n void Promise.resolve()\n .then(() => {\n if (path === '') throw new Error('Invalid image path.');\n return loader(path);\n })\n .then((result) => {\n if (!active) return result.dispose();\n asset = result;\n setLoaded({ path, url: result.url });\n })\n .catch(() => {\n // The failure is shown in place, as the web image does.\n if (active) setLoaded({ path, failed: true });\n });\n return () => {\n active = false;\n asset?.dispose();\n };\n }, [path, loader]);\n const current = loaded?.path === path ? loaded : undefined;\n const uri = path === undefined ? (DIRECT_IMAGE.test(src) ? src : undefined) : current?.url;\n if (uri === undefined || current?.failed) {\n const waiting = path !== undefined && loader !== undefined && current === undefined;\n return (\n <Text style={[styles.prose, styles.imageState]} accessibilityRole={waiting ? 'text' : 'alert'}>\n {alt || path || 'Image'} ({waiting ? 'loading\\u2026' : 'unavailable'})\n </Text>\n );\n }\n return (\n <Image\n source={{ uri }}\n accessibilityLabel={alt || 'Image'}\n accessibilityIgnoresInvertColors\n resizeMode=\"contain\"\n onLoad={({ nativeEvent }) => {\n const fit = (width: number, height: number) => {\n if (width > 0 && height > 0) setRatio(width / height);\n };\n // iOS and Android report the size with the event; react-native-web does not, so it asks.\n const size = (nativeEvent as { source?: { width: number; height: number } } | undefined)?.source;\n if (size !== undefined) fit(size.width, size.height);\n else Image.getSize(uri, fit, () => undefined);\n }}\n onError={() => setLoaded({ path: path ?? src, failed: true })}\n style={[styles.image, { aspectRatio: ratio }]}\n />\n );\n}\nfunction renderInline(nodes: readonly MarkdownInline[]): ReactNode[] {\n return nodes.map((node, index) => {\n switch (node.type) {\n case 'text':\n return node.text;\n case 'code':\n return <InlineCode key={index} text={node.text} />;\n case 'strong':\n return (\n <Text key={index} style={styles.strong}>\n {renderInline(node.children)}\n </Text>\n );\n case 'em':\n return (\n <Text key={index} style={styles.em}>\n {renderInline(node.children)}\n </Text>\n );\n case 'link':\n return (\n <InlineLink key={index} href={node.href}>\n {renderInline(node.children)}\n </InlineLink>\n );\n case 'image':\n // Inside a heading or list item an image keeps to its alt text; paragraphs draw it.\n return node.alt;\n }\n });\n}\n\n/**\n * A table read column by column: every cell keeps to one line inside the sideways scroll, so each\n * column takes its widest cell and the rows line up without a table layout.\n */\nfunction MarkdownTable({ header, rows }: { header: MarkdownInline[][]; rows: MarkdownInline[][][] }) {\n return (\n <ScrollView horizontal showsHorizontalScrollIndicator={false}>\n <View style={styles.table}>\n {header.map((cell, column) => (\n <View key={column}>\n <Text style={[styles.prose, styles.cell, styles.headCell]}>{renderInline(cell)}</Text>\n {rows.map((row, index) => (\n <Text key={index} style={[styles.prose, styles.cell]}>\n {renderInline(row[column] ?? [])}\n </Text>\n ))}\n </View>\n ))}\n </View>\n </ScrollView>\n );\n}\n\nfunction Block({ block, first, quoted = false }: { block: MarkdownBlock; first: boolean; quoted?: boolean }) {\n const prose = [styles.prose, quoted && styles.quoted];\n switch (block.type) {\n case 'heading':\n return (\n <Text\n accessibilityRole=\"header\"\n style={[...prose, styles.heading(block.level), first && styles.firstHeading, quoted && styles.quoted]}\n >\n {renderInline(block.children)}\n </Text>\n );\n case 'paragraph': {\n // An image breaks the paragraph's text into runs, since an Image cannot sit inside Text.\n const parts: ReactNode[] = [];\n let run: MarkdownInline[] = [];\n const flush = () => {\n if (run.length > 0)\n parts.push(\n <Text key={parts.length} style={prose}>\n {renderInline(run)}\n </Text>,\n );\n run = [];\n };\n for (const node of block.children) {\n if (node.type !== 'image') {\n run.push(node);\n continue;\n }\n flush();\n parts.push(<MarkdownImage key={parts.length} src={node.src} alt={node.alt} />);\n }\n flush();\n return parts.length === 1 ? parts[0] : <View style={styles.root}>{parts}</View>;\n }\n case 'list':\n return (\n <View accessibilityRole=\"list\" style={styles.list}>\n {block.items.map((item, index) => (\n <View key={index} style={styles.item}>\n <Text style={[...prose, styles.marker]}>{block.ordered ? `${block.start + index}.` : '\\u2022'}</Text>\n <Text style={[...prose, styles.itemText]}>{renderInline(item)}</Text>\n </View>\n ))}\n </View>\n );\n case 'code':\n return <CodeBlock text={block.text} language={block.language} />;\n case 'rule':\n return <View accessible={false} style={styles.rule} />;\n case 'quote':\n return (\n <View style={[styles.root, styles.quote]}>\n {block.children.map((child, index) => (\n <Block key={index} block={child} first={index === 0} quoted />\n ))}\n </View>\n );\n case 'table':\n return <MarkdownTable header={block.header} rows={block.rows} />;\n }\n}\n\nexport interface MarkdownProps {\n text: string;\n onFileLink?: FileLinkHandler;\n /** Loads relative image paths; without it they show as unavailable. */\n loadImage?: MarkdownImageLoader;\n}\n\n/** Markdown in Po's shared presentation, parsed by the package's own small parser. */\nexport const Markdown = memo(function Markdown({ text, onFileLink, loadImage }: MarkdownProps) {\n const blocks = useMemo(() => parseMarkdown(text), [text]);\n return (\n <FileLinkContext.Provider value={onFileLink}>\n <ImageContext.Provider value={loadImage}>\n <View style={styles.root}>\n {blocks.map((block, index) => (\n <Block key={index} block={block} first={index === 0} />\n ))}\n </View>\n </ImageContext.Provider>\n </FileLinkContext.Provider>\n );\n});\n\n/**\n * Headings step up from the prose and never shrink below it; h4 to h6 keep\n * the prose size and lean on weight, fading from `hi` to `dim`.\n */\nconst HEADING_STEP = { 1: '2xl', 2: 'xl', 3: 'lg', 4: 'base', 5: 'base', 6: 'base' } as const;\nconst HEADING_SPACE = { 1: 2, 2: 2, 3: 1, 4: 0, 5: 0, 6: 0 } as const;\nconst HEADING_INK = { 1: 'hi', 2: 'hi', 3: 'hi', 4: 'hi', 5: 'text', 6: 'dim' } as const;\n\nconst styles = StyleSheet.create((theme) => ({\n root: { minWidth: 0, gap: theme.spacing(2) },\n prose: {\n color: theme.colors.text,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n },\n heading: (level: MarkdownHeadingLevel) => ({\n marginTop: theme.spacing(HEADING_SPACE[level]),\n fontSize: theme.fontSize[HEADING_STEP[level]],\n lineHeight: theme.lineHeight[HEADING_STEP[level]],\n letterSpacing: theme.letterSpacing[HEADING_STEP[level]],\n fontWeight: theme.fontWeight.semibold,\n color: theme.colors[HEADING_INK[level]],\n }),\n firstHeading: { marginTop: 0 },\n strong: { fontWeight: theme.fontWeight.semibold, color: theme.colors.hi },\n em: { fontStyle: 'italic' },\n code: {\n backgroundColor: theme.colors.deep,\n color: theme.colors.green,\n fontFamily: fontOf(theme.fontFamily.mono),\n },\n fileLink: { textDecorationLine: 'underline' },\n link: { color: theme.colors.blue, textDecorationLine: 'underline' },\n list: { gap: theme.spacing(0.5) },\n item: { flexDirection: 'row', gap: theme.spacing(2), paddingLeft: theme.spacing(1) },\n marker: { minWidth: theme.spacing(3), color: theme.colors.dim },\n itemText: { flexShrink: 1 },\n rule: { height: 1, backgroundColor: theme.colors['border-soft'] },\n // The web blockquote: a rule at the left and quieter text.\n quote: { borderLeftWidth: 2, borderLeftColor: theme.colors.border, paddingLeft: theme.spacing(3) },\n quoted: { color: theme.colors.dim },\n // The web table: bordered cells, the header on the deep surface.\n table: {\n flexDirection: 'row',\n borderTopWidth: 1,\n borderLeftWidth: 1,\n borderColor: theme.colors.border,\n },\n cell: {\n paddingHorizontal: theme.spacing(2),\n paddingVertical: theme.spacing(1),\n borderRightWidth: 1,\n borderBottomWidth: 1,\n borderColor: theme.colors.border,\n },\n headCell: { backgroundColor: theme.colors.deep, fontWeight: theme.fontWeight.semibold, color: theme.colors.hi },\n // The web image's frame: at most 480 tall, a soft border and rounded corners.\n image: {\n width: '100%',\n maxHeight: 480,\n borderRadius: theme.borderRadius.md,\n borderWidth: 1,\n borderColor: theme.colors.border,\n },\n imageState: { color: theme.colors.dim },\n}));\n"],"mappings":"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"}
@@ -35,22 +35,21 @@ export interface MessageItemProps extends Omit<ViewProps, 'children' | 'style'>
35
35
  export declare function MessageItem({ tone, expandable, defaultExpanded, expanded, onExpandedChange, children, style, ...props }: MessageItemProps): import("react").JSX.Element;
36
36
  export interface MessageItemGroupProps extends Omit<ViewProps, 'children' | 'style'> {
37
37
  tone?: StatusTone;
38
- /** The bold label at the left of the group header: the tool the run belongs to. */
38
+ /** The bold label at the left of the group header, such as "7 tool calls". */
39
39
  title: ReactNode;
40
- /** The line beside it, such as how many calls the group holds. */
40
+ /** The line beside it, such as which tools the group holds. */
41
41
  summary?: ReactNode;
42
- /** The badge text; defaults to the tone's label, null hides the badge. */
42
+ /** The outcome's label; defaults to the tone's label, null hides the outcome. */
43
43
  badge?: ReactNode | null;
44
- defaultExpanded?: boolean;
45
44
  children?: ReactNode;
46
45
  style?: StyleProp<ViewStyle>;
47
46
  }
48
47
  /**
49
- * One frame around a run of items that belong together. Each item inside
50
- * becomes a row: same header, same expand, no card of its own, and its tone
51
- * an edge down its left side.
48
+ * One frame around a run of items that belong together. It stays open: each
49
+ * item inside becomes a row with its own name, outcome and expand, no card of
50
+ * its own, and its tone an edge down its left side.
52
51
  */
53
- export declare function MessageItemGroup({ tone, title, summary, badge, children, defaultExpanded, style, ...props }: MessageItemGroupProps): import("react").JSX.Element;
52
+ export declare function MessageItemGroup({ tone, title, summary, badge, children, style, ...props }: MessageItemGroupProps): import("react").JSX.Element;
54
53
  export interface MessageItemHeaderProps extends Omit<ViewProps, 'children' | 'style'> {
55
54
  /** The bold label at the left: the tool name. */
56
55
  title: ReactNode;
@@ -61,9 +60,9 @@ export interface MessageItemHeaderProps extends Omit<ViewProps, 'children' | 'st
61
60
  style?: StyleProp<ViewStyle>;
62
61
  }
63
62
  /**
64
- * The title row: label, summary, the tone's badge and, when the item is
63
+ * The title row: label, summary, the tone's outcome and, when the item is
65
64
  * expandable, a chevron; the whole row is the toggle. A row inside a group
66
- * drops its title and speaks up only when something failed or is running.
65
+ * keeps its name but drops the chip and caption the group header carries.
67
66
  */
68
67
  export declare function MessageItemHeader({ title, badge, children, style, ...props }: MessageItemHeaderProps): import("react").JSX.Element;
69
68
  export interface MessageItemPartProps extends Omit<ViewProps, 'children' | 'style'> {
@@ -1 +1 @@
1
- {"version":3,"file":"MessageItem.d.mts","names":[],"sources":["../../src/components/MessageItem.tsx"],"mappings":";;;;iBA0BiB;EACf,MAAM;;EAEN;EACA;EACA;;;wBAWc,kBAAkB;;wBAQlB,SAAS;EAAS;EAAkB;IAAqB;;qBAK5D,cAAc,SAAS,OAAO;;qBAU9B,cAAc,SAAS,OAAO;iBAmB1B,yBAAyB,KAAK;EAC7C,OAAO;;EAEP;EACA;;EAEA;EACA,oBAAoB;;EAEpB,WAAW,cAAc,OAAO,qBAAqB;EACrD,QAAQ,UAAU;;wBAGJ,cACd,MACA,YACA,iBACA,UACA,kBACA,UACA,UACG,SACF,mCAAgB,IAAA;iBA8DF,8BAA8B,KAAK;EAClD,OAAO;;EAEP,OAAO;;EAEP,UAAU;;EAEV,QAAQ;EACR;EACA,WAAW;EACX,QAAQ,UAAU;;;;;;;wBAQJ,mBACd,MACA,OACA,SACA,OACA,UACA,iBACA,UACG,SACF,wCAAqB,IAAA;iBAyBP,+BAA+B,KAAK;;EAEnD,OAAO;;EAEP,QAAQ;;EAER,WAAW;EACX,QAAQ,UAAU;;;;;;;wBAWJ,oBAAoB,OAAO,OAAO,UAAU,UAAU,SAAS,yCAAsB,IAAA;iBAiCpF,6BAA6B,KAAK;EACjD,WAAW;EACX,QAAQ,UAAU;;;wBAIJ,kBAAkB,UAAU,UAAU,SAAS,uCAAoB,IAAA;iBAQlE,+BAA+B,KAAK;EACnD,OAAO;;EAEP;;EAEA;EACA,WAAW;;EAEX,iBAAiB,UAAU;;;wBAIb,oBACd,MACA,OACA,SACA,UACA,gBACA,UACG,SACF,yCAAsB,IAAA"}
1
+ {"version":3,"file":"MessageItem.d.mts","names":[],"sources":["../../src/components/MessageItem.tsx"],"mappings":";;;;iBA6BiB;EACf,MAAM;;EAEN;EACA;EACA;;;wBAWc,kBAAkB;;wBAQlB,SAAS;EAAS;EAAkB;IAAqB;;qBAK5D,cAAc,SAAS,OAAO;;qBAU9B,cAAc,SAAS,OAAO;iBAmB1B,yBAAyB,KAAK;EAC7C,OAAO;;EAEP;EACA;;EAEA;EACA,oBAAoB;;EAEpB,WAAW,cAAc,OAAO,qBAAqB;EACrD,QAAQ,UAAU;;wBAGJ,cACd,MACA,YACA,iBACA,UACA,kBACA,UACA,UACG,SACF,mCAAgB,IAAA;iBA2FF,8BAA8B,KAAK;EAClD,OAAO;;EAEP,OAAO;;EAEP,UAAU;;EAEV,QAAQ;EACR,WAAW;EACX,QAAQ,UAAU;;;;;;;wBAQJ,mBACd,MACA,OACA,SACA,OACA,UACA,UACG,SACF,wCAAqB,IAAA;iBAmBP,+BAA+B,KAAK;;EAEnD,OAAO;;EAEP,QAAQ;;EAER,WAAW;EACX,QAAQ,UAAU;;;;;;;wBAQJ,oBAAoB,OAAO,OAAO,UAAU,UAAU,SAAS,yCAAsB,IAAA;iBAoCpF,6BAA6B,KAAK;EACjD,WAAW;EACX,QAAQ,UAAU;;;wBAIJ,kBAAkB,UAAU,UAAU,SAAS,uCAAoB,IAAA;iBAQlE,+BAA+B,KAAK;EACnD,OAAO;;EAEP;;EAEA;EACA,WAAW;;EAEX,iBAAiB,UAAU;;;wBAIb,oBACd,MACA,OACA,SACA,UACA,gBACA,UACG,SACF,yCAAsB,IAAA"}
@@ -9,11 +9,17 @@ import { STATUS_EDGE, StatusBadge } from "./StatusBadge.mjs";
9
9
  import { View } from "react-native-unistyles/components/native/View";
10
10
  import { Text } from "react-native-unistyles/components/native/Text";
11
11
  import { StyleSheet } from "react-native-unistyles";
12
- import { createContext, useContext } from "react";
12
+ import { createContext, useContext, useMemo } from "react";
13
13
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
14
14
  import { Pressable } from "react-native-unistyles/components/native/Pressable";
15
15
 
16
16
  //#region src/components/MessageItem.tsx
17
+ /** Tones whose outcome reads as an icon alone; the rest keep their worded badge. */
18
+ const ICON_TONES = /* @__PURE__ */ new Set([
19
+ "running",
20
+ "ok",
21
+ "error"
22
+ ]);
17
23
  const MIN_ROW_HEIGHT = 44;
18
24
  const LEAD_CHIP = 36;
19
25
  const ROW_EDGE_WIDTH = 2;
@@ -89,87 +95,102 @@ function Summary({ children }) {
89
95
  });
90
96
  }
91
97
  /** The card's lead: the tool chip, the "Tool call" caption, then the name and its summary on one line. */
92
- function ToolCallLead({ title, children }) {
98
+ function ToolCallLead({ title, caption = "Tool call", children }) {
93
99
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(View, {
94
100
  accessible: false,
95
101
  style: styles.chip,
96
102
  children: /* @__PURE__ */ jsx(WrenchIcon, { size: 16 })
97
103
  }), /* @__PURE__ */ jsxs(View, {
98
104
  style: styles.lead,
99
- children: [/* @__PURE__ */ jsx(Text, {
105
+ children: [caption === null ? null : /* @__PURE__ */ jsx(Text, {
100
106
  style: styles.caption,
101
- children: "Tool call"
107
+ children: caption
102
108
  }), /* @__PURE__ */ jsxs(View, {
103
109
  style: styles.titleLine,
104
110
  children: [wrapText(title, styles.title), /* @__PURE__ */ jsx(Summary, { children })]
105
111
  })]
106
112
  })] });
107
113
  }
108
- /** The sentence-case outcome pill, led by the tone's icon. */
109
- function OutcomeBadge({ tone, children }) {
114
+ /** A row's lead inside a group: the name and its summary, no chip or caption, since the group header carries them. */
115
+ function RowLead({ title, children }) {
116
+ return /* @__PURE__ */ jsxs(View, {
117
+ style: [styles.lead, styles.titleLine],
118
+ children: [wrapText(title, styles.title), /* @__PURE__ */ jsx(Summary, { children })]
119
+ });
120
+ }
121
+ /** The outcome: an icon for running, done and failed, announced by its label; a worded badge for other tones. */
122
+ function Outcome({ tone, children }) {
110
123
  const ink = STATUS_COLORS[tone].ink;
111
- return /* @__PURE__ */ jsxs(StatusBadge, {
112
- tone,
113
- size: "md",
114
- style: styles.outcome,
124
+ if (ICON_TONES.has(tone)) return /* @__PURE__ */ jsxs(View, {
125
+ accessible: true,
126
+ accessibilityRole: "image",
127
+ accessibilityLabel: typeof children === "string" ? children : STATUS_LABEL[tone],
128
+ style: styles.outcomeIcon,
115
129
  children: [
116
130
  tone === "running" ? /* @__PURE__ */ jsx(Spinner, { color: ink }) : null,
117
131
  tone === "ok" ? /* @__PURE__ */ jsx(CheckIcon, {
118
- size: 12,
132
+ size: 14,
119
133
  color: ink
120
134
  }) : null,
121
135
  tone === "error" ? /* @__PURE__ */ jsx(AlertIcon, {
122
- size: 12,
136
+ size: 14,
123
137
  color: ink
124
- }) : null,
125
- children
138
+ }) : null
126
139
  ]
127
140
  });
141
+ return /* @__PURE__ */ jsx(StatusBadge, {
142
+ tone,
143
+ size: "md",
144
+ style: styles.outcome,
145
+ children
146
+ });
128
147
  }
129
148
  /**
130
- * One frame around a run of items that belong together. Each item inside
131
- * becomes a row: same header, same expand, no card of its own, and its tone
132
- * an edge down its left side.
149
+ * One frame around a run of items that belong together. It stays open: each
150
+ * item inside becomes a row with its own name, outcome and expand, no card of
151
+ * its own, and its tone an edge down its left side.
133
152
  */
134
- function MessageItemGroup({ tone = "neutral", title, summary, badge, children, defaultExpanded = false, style, ...props }) {
135
- return /* @__PURE__ */ jsx(MessageItem, {
153
+ function MessageItemGroup({ tone = "neutral", title, summary, badge, children, style, ...props }) {
154
+ const label = badge === void 0 ? STATUS_LABEL[tone] : badge;
155
+ const group = useMemo(() => ({ tone }), [tone]);
156
+ return /* @__PURE__ */ jsxs(MessageItem, {
136
157
  tone,
137
- expandable: true,
138
- defaultExpanded,
139
158
  style,
140
159
  ...props,
141
- children: ({ expanded }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MessageItemHeader, {
142
- title,
143
- badge,
144
- children: typeof summary === "string" ? /* @__PURE__ */ jsx(Text, {
145
- numberOfLines: 1,
146
- style: styles.faint,
160
+ children: [/* @__PURE__ */ jsxs(View, {
161
+ style: styles.header,
162
+ children: [/* @__PURE__ */ jsx(ToolCallLead, {
163
+ title,
164
+ caption: null,
147
165
  children: summary
148
- }) : summary
149
- }), expanded ? /* @__PURE__ */ jsx(MessageItemGroupContext.Provider, {
150
- value: { tone },
166
+ }), label !== null && label !== "" ? /* @__PURE__ */ jsx(Outcome, {
167
+ tone,
168
+ children: label
169
+ }) : null]
170
+ }), /* @__PURE__ */ jsx(MessageItemGroupContext.Provider, {
171
+ value: group,
151
172
  children: /* @__PURE__ */ jsx(View, { children })
152
- }) : null] })
173
+ })]
153
174
  });
154
175
  }
155
- /** Inside a group these two are the quiet outcome: the group's header has already said it. */
156
- const SILENT_IN_GROUP = /* @__PURE__ */ new Set(["ok", "neutral"]);
157
176
  /**
158
- * The title row: label, summary, the tone's badge and, when the item is
177
+ * The title row: label, summary, the tone's outcome and, when the item is
159
178
  * expandable, a chevron; the whole row is the toggle. A row inside a group
160
- * drops its title and speaks up only when something failed or is running.
179
+ * keeps its name but drops the chip and caption the group header carries.
161
180
  */
162
181
  function MessageItemHeader({ title, badge, children, style, ...props }) {
163
182
  const { tone, expandable, expanded, toggle } = useMessageItem();
164
183
  const group = useContext(MessageItemGroupContext);
165
- const defaultLabel = group !== null && SILENT_IN_GROUP.has(tone) ? null : STATUS_LABEL[tone];
166
- const label = badge === void 0 ? defaultLabel : badge;
184
+ const label = badge === void 0 ? STATUS_LABEL[tone] : badge;
167
185
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
168
186
  group === null ? /* @__PURE__ */ jsx(ToolCallLead, {
169
187
  title,
170
188
  children
171
- }) : /* @__PURE__ */ jsx(Summary, { children }),
172
- label !== null && label !== "" ? /* @__PURE__ */ jsx(OutcomeBadge, {
189
+ }) : /* @__PURE__ */ jsx(RowLead, {
190
+ title,
191
+ children
192
+ }),
193
+ label !== null && label !== "" ? /* @__PURE__ */ jsx(Outcome, {
173
194
  tone,
174
195
  children: label
175
196
  }) : null,
@@ -311,18 +332,17 @@ const styles = StyleSheet.create((theme) => ({
311
332
  lineHeight: theme.lineHeight.base,
312
333
  uni__dependencies: [0]
313
334
  },
314
- faint: {
315
- flexShrink: 1,
316
- color: theme.colors.faint,
317
- fontFamily: fontOf(theme.fontFamily.sans),
318
- fontSize: theme.fontSize.base,
319
- lineHeight: theme.lineHeight.base,
320
- uni__dependencies: [0]
321
- },
322
335
  outcome: {
323
336
  gap: theme.spacing(1),
324
337
  uni__dependencies: [0]
325
338
  },
339
+ outcomeIcon: {
340
+ alignItems: "center",
341
+ justifyContent: "center",
342
+ width: 20,
343
+ height: 20,
344
+ flexShrink: 0
345
+ },
326
346
  body: {
327
347
  gap: theme.spacing(1),
328
348
  borderTopWidth: 1,
@@ -1 +1 @@
1
- {"version":3,"file":"MessageItem.mjs","names":["NativeColorToken","createContext","ReactNode","useContext","StyleProp","TextProps","ViewProps","ViewStyle","StyleSheet","AlertIcon","CheckIcon","ChevronDownIcon","ChevronRightIcon","WrenchIcon","fontOf","STATUS_COLORS","useControllable","wrapText","Button","Spinner","STATUS_EDGE","StatusBadge","StatusTone","MIN_ROW_HEIGHT","LEAD_CHIP","ROW_EDGE_WIDTH","MessageItemState","tone","expandable","expanded","toggle","MessageItemContext","undefined","useMessageItem","MessageItemGroupContext","toolTone","state","running","isError","STATUS_LABEL","Readonly","Record","neutral","ok","error","info","accent","STATUS_GLYPH","cardEdge","rowEdge","MessageItemProps","Omit","defaultExpanded","onExpandedChange","children","style","MessageItem","props","current","setExpanded","group","styles","Summary","summary","summaryText","ToolCallLead","title","chip","lead","caption","titleLine","OutcomeBadge","ink","outcome","MessageItemGroupProps","badge","MessageItemGroup","faint","MessageItemHeaderProps","SILENT_IN_GROUP","ReadonlySet","Set","MessageItemHeader","defaultLabel","has","label","content","header","pressed","headerPressed","MessageItemPartProps","MessageItemBody","body","bodyText","MessageItemStatusProps","glyph","expands","containerStyle","MessageItemStatus","line","status","more","create","theme","token","overflow","borderRadius","lg","borderWidth","borderColor","colors","uni__dependencies","borderTopWidth","borderTopColor","borderLeftWidth","borderLeftColor","flexDirection","alignItems","gap","spacing","minHeight","paddingHorizontal","paddingVertical","backgroundColor","panel","justifyContent","width","height","flexShrink","full","rail","flex","minWidth","color","dim","fontFamily","sans","fontSize","sm","lineHeight","hi","base","fontWeight","semibold","alignSelf"],"sources":["../../src/components/MessageItem.tsx"],"sourcesContent":["import type { NativeColorToken } from '@agimon-ai/po-web-components/nativeTheme';\nimport { createContext, type ReactNode, useContext } from 'react';\nimport { Pressable, type StyleProp, Text, type TextProps, View, type ViewProps, type ViewStyle } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { AlertIcon, CheckIcon, ChevronDownIcon, ChevronRightIcon, WrenchIcon } from '../icons/icons';\nimport { fontOf } from '../lib/fonts';\nimport { STATUS_COLORS } from '../lib/tone';\nimport { useControllable } from '../lib/useControllable';\nimport { wrapText } from '../lib/wrapText';\nimport { Button } from './Button';\nimport { Spinner } from './Spinner';\nimport { STATUS_EDGE, StatusBadge, type StatusTone } from './StatusBadge';\n\n/*\n * The message item: the timeline card a tool call, a run or a notice sits in.\n * The item owns its outcome tone and its expand state; the header, body and\n * status parts read both from it, so a card composes the parts and never\n * threads `expanded` by hand. The header is one Pressable that expands the\n * item, the native stand-in for the web's small chevron button.\n */\n\nconst MIN_ROW_HEIGHT = 44;\nconst LEAD_CHIP = 36;\nconst ROW_EDGE_WIDTH = 2;\n\nexport interface MessageItemState {\n tone: StatusTone;\n /** Whether the header offers the toggle; an item that hides nothing offers none. */\n expandable: boolean;\n expanded: boolean;\n toggle: () => void;\n}\n\nconst MessageItemContext = createContext<MessageItemState>({\n tone: 'neutral',\n expandable: false,\n expanded: false,\n toggle: () => undefined,\n});\n\n/** The enclosing item's state, for a part that lives deeper than the item's render prop. */\nexport function useMessageItem(): MessageItemState {\n return useContext(MessageItemContext);\n}\n\n/** The group an item is a row of, or null for an item that is a card of its own. */\nconst MessageItemGroupContext = createContext<{ tone: StatusTone } | null>(null);\n\n/** The tone a tool call's item wears: running while the tool runs, then its outcome. */\nexport function toolTone(state: { running: boolean; isError: boolean }): StatusTone {\n return state.running ? 'running' : state.isError ? 'error' : 'ok';\n}\n\n/** The badge text a tone reads as when the header is not told otherwise; empty hides the badge. */\nexport const STATUS_LABEL: Readonly<Record<StatusTone, string>> = {\n neutral: '',\n running: 'Running',\n ok: 'Completed',\n error: 'Failed',\n info: 'Info',\n accent: 'Note',\n};\n\n/** The glyph a tone reads as in a status line. */\nexport const STATUS_GLYPH: Readonly<Record<StatusTone, string>> = {\n neutral: '\\u2026',\n running: '\\u25d0',\n ok: '\\u2713',\n error: '\\u2717',\n info: '\\u25cf',\n accent: '\\u25cf',\n};\n\n/** A settled OK is the calm default and keeps the soft border; only work in flight or a failure colours the edge. */\nfunction cardEdge(tone: StatusTone): NativeColorToken {\n return tone === 'ok' ? 'border-soft' : STATUS_EDGE[tone];\n}\n\n/** As a row, a settled or neutral item keeps a quiet edge. */\nfunction rowEdge(tone: StatusTone): NativeColorToken | undefined {\n return tone === 'ok' || tone === 'neutral' ? undefined : STATUS_EDGE[tone];\n}\n\nexport interface MessageItemProps extends Omit<ViewProps, 'children' | 'style'> {\n tone?: StatusTone;\n /** Whether the header offers the expand toggle. A toggle that reveals nothing reads as broken, so the card decides. */\n expandable?: boolean;\n defaultExpanded?: boolean;\n /** Controlled mode; pair with onExpandedChange. Omit it and the item keeps its own state. */\n expanded?: boolean;\n onExpandedChange?: (expanded: boolean) => void;\n /** Plain content, or a function of the item's state for a card that picks its lines by `expanded`. */\n children?: ReactNode | ((state: MessageItemState) => ReactNode);\n style?: StyleProp<ViewStyle>;\n}\n\nexport function MessageItem({\n tone = 'neutral',\n expandable = false,\n defaultExpanded = false,\n expanded,\n onExpandedChange,\n children,\n style,\n ...props\n}: MessageItemProps) {\n const [current, setExpanded] = useControllable(expanded, defaultExpanded, onExpandedChange);\n const group = useContext(MessageItemGroupContext);\n const state: MessageItemState = { tone, expandable, expanded: current, toggle: () => setExpanded(!current) };\n return (\n <MessageItemContext.Provider value={state}>\n <View\n style={[group === null ? styles.cardEdge(cardEdge(tone)) : styles.rowEdge(rowEdge(tone)), style]}\n {...props}\n >\n {typeof children === 'function' ? children(state) : children}\n </View>\n </MessageItemContext.Provider>\n );\n}\n\n/** The line beside the title; a bare string stays on one line and truncates, as the web's `truncate`. */\nfunction Summary({ children }: { children?: ReactNode }) {\n return (\n <View style={styles.summary}>\n {typeof children === 'string' ? (\n <Text numberOfLines={1} style={styles.summaryText}>\n {children}\n </Text>\n ) : (\n wrapText(children, styles.summaryText)\n )}\n </View>\n );\n}\n\n/** The card's lead: the tool chip, the \"Tool call\" caption, then the name and its summary on one line. */\nfunction ToolCallLead({ title, children }: { title: ReactNode; children?: ReactNode }) {\n return (\n <>\n <View accessible={false} style={styles.chip}>\n <WrenchIcon size={16} />\n </View>\n <View style={styles.lead}>\n <Text style={styles.caption}>Tool call</Text>\n <View style={styles.titleLine}>\n {wrapText(title, styles.title)}\n <Summary>{children}</Summary>\n </View>\n </View>\n </>\n );\n}\n\n/** The sentence-case outcome pill, led by the tone's icon. */\nfunction OutcomeBadge({ tone, children }: { tone: StatusTone; children: ReactNode }) {\n const ink = STATUS_COLORS[tone].ink;\n return (\n <StatusBadge tone={tone} size=\"md\" style={styles.outcome}>\n {tone === 'running' ? <Spinner color={ink} /> : null}\n {tone === 'ok' ? <CheckIcon size={12} color={ink} /> : null}\n {tone === 'error' ? <AlertIcon size={12} color={ink} /> : null}\n {children}\n </StatusBadge>\n );\n}\n\nexport interface MessageItemGroupProps extends Omit<ViewProps, 'children' | 'style'> {\n tone?: StatusTone;\n /** The bold label at the left of the group header: the tool the run belongs to. */\n title: ReactNode;\n /** The line beside it, such as how many calls the group holds. */\n summary?: ReactNode;\n /** The badge text; defaults to the tone's label, null hides the badge. */\n badge?: ReactNode | null;\n defaultExpanded?: boolean;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * One frame around a run of items that belong together. Each item inside\n * becomes a row: same header, same expand, no card of its own, and its tone\n * an edge down its left side.\n */\nexport function MessageItemGroup({\n tone = 'neutral',\n title,\n summary,\n badge,\n children,\n defaultExpanded = false,\n style,\n ...props\n}: MessageItemGroupProps) {\n return (\n <MessageItem tone={tone} expandable defaultExpanded={defaultExpanded} style={style} {...props}>\n {({ expanded }) => (\n <>\n <MessageItemHeader title={title} badge={badge}>\n {typeof summary === 'string' ? (\n <Text numberOfLines={1} style={styles.faint}>\n {summary}\n </Text>\n ) : (\n summary\n )}\n </MessageItemHeader>\n {expanded ? (\n <MessageItemGroupContext.Provider value={{ tone }}>\n <View>{children}</View>\n </MessageItemGroupContext.Provider>\n ) : null}\n </>\n )}\n </MessageItem>\n );\n}\n\nexport interface MessageItemHeaderProps extends Omit<ViewProps, 'children' | 'style'> {\n /** The bold label at the left: the tool name. */\n title: ReactNode;\n /** The badge text; defaults to the tone's label, null hides the badge. */\n badge?: ReactNode | null;\n /** The summary beside the title; it gets the remaining width. */\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/** Inside a group these two are the quiet outcome: the group's header has already said it. */\nconst SILENT_IN_GROUP: ReadonlySet<StatusTone> = new Set<StatusTone>(['ok', 'neutral']);\n\n/**\n * The title row: label, summary, the tone's badge and, when the item is\n * expandable, a chevron; the whole row is the toggle. A row inside a group\n * drops its title and speaks up only when something failed or is running.\n */\nexport function MessageItemHeader({ title, badge, children, style, ...props }: MessageItemHeaderProps) {\n const { tone, expandable, expanded, toggle } = useMessageItem();\n const group = useContext(MessageItemGroupContext);\n const defaultLabel = group !== null && SILENT_IN_GROUP.has(tone) ? null : STATUS_LABEL[tone];\n const label = badge === undefined ? defaultLabel : badge;\n const content = (\n <>\n {group === null ? <ToolCallLead title={title}>{children}</ToolCallLead> : <Summary>{children}</Summary>}\n {label !== null && label !== '' ? <OutcomeBadge tone={tone}>{label}</OutcomeBadge> : null}\n {expandable ? expanded ? <ChevronDownIcon size={16} /> : <ChevronRightIcon size={16} /> : null}\n </>\n );\n if (!expandable) {\n return (\n <View style={[styles.header, style]} {...props}>\n {content}\n </View>\n );\n }\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ expanded }}\n accessibilityHint={expanded ? 'collapse' : 'expand'}\n onPress={toggle}\n style={({ pressed }) => [styles.header, pressed && styles.headerPressed, style]}\n {...props}\n >\n {content}\n </Pressable>\n );\n}\n\nexport interface MessageItemPartProps extends Omit<ViewProps, 'children' | 'style'> {\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/** The body under the header, on the card's own surface. */\nexport function MessageItemBody({ children, style, ...props }: MessageItemPartProps) {\n return (\n <View style={[styles.body, style]} {...props}>\n {wrapText(children, styles.bodyText)}\n </View>\n );\n}\n\nexport interface MessageItemStatusProps extends Omit<TextProps, 'children'> {\n tone?: StatusTone;\n /** Replaces the tone's glyph. */\n glyph?: string;\n /** Renders as a button that expands the item: the \"N more line(s)\" hint. */\n expands?: boolean;\n children?: ReactNode;\n /** Applied to the row; with `expands`, to the button. */\n containerStyle?: StyleProp<ViewStyle>;\n}\n\n/** The one-line status under a body: `running`, `failed`, `done`, or the hint that more is hidden. */\nexport function MessageItemStatus({\n tone = 'neutral',\n glyph,\n expands = false,\n children,\n containerStyle,\n style,\n ...props\n}: MessageItemStatusProps) {\n const { toggle } = useMessageItem();\n const line = (\n <Text style={[styles.status, style]} {...props}>\n <Text style={styles.glyph(tone)}>{glyph ?? STATUS_GLYPH[tone]}</Text> {children}\n </Text>\n );\n if (expands) {\n return (\n <Button variant=\"ghost\" size=\"xs\" onPress={toggle} style={[styles.more, containerStyle]}>\n {line}\n </Button>\n );\n }\n return <View style={containerStyle}>{line}</View>;\n}\n\nconst styles = StyleSheet.create((theme) => ({\n cardEdge: (token: NativeColorToken) => ({\n overflow: 'hidden',\n borderRadius: theme.borderRadius.lg,\n borderWidth: 1,\n borderColor: theme.colors[token],\n }),\n // A row's top rule is the group's separator; its left edge carries the tone.\n rowEdge: (token: NativeColorToken | undefined) => ({\n overflow: 'hidden',\n borderTopWidth: 1,\n borderTopColor: theme.colors['border-soft'],\n borderLeftWidth: ROW_EDGE_WIDTH,\n borderLeftColor: token === undefined ? 'transparent' : theme.colors[token],\n }),\n header: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.spacing(3),\n minHeight: MIN_ROW_HEIGHT,\n paddingHorizontal: theme.spacing(4),\n paddingVertical: theme.spacing(3),\n },\n headerPressed: { backgroundColor: theme.colors.panel },\n chip: {\n alignItems: 'center',\n justifyContent: 'center',\n width: LEAD_CHIP,\n height: LEAD_CHIP,\n flexShrink: 0,\n borderRadius: theme.borderRadius.full,\n backgroundColor: theme.colors.rail,\n },\n lead: { flex: 1, minWidth: 0 },\n caption: {\n color: theme.colors.dim,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n titleLine: { flexDirection: 'row', alignItems: 'center', gap: theme.spacing(2), minWidth: 0 },\n title: {\n flexShrink: 0,\n color: theme.colors.hi,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n fontWeight: theme.fontWeight.semibold,\n },\n summary: { flex: 1, flexDirection: 'row', alignItems: 'center', gap: theme.spacing(2), minWidth: 0 },\n summaryText: {\n flexShrink: 1,\n color: theme.colors.faint,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n },\n faint: {\n flexShrink: 1,\n color: theme.colors.faint,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n },\n outcome: { gap: theme.spacing(1) },\n body: {\n gap: theme.spacing(1),\n borderTopWidth: 1,\n borderTopColor: theme.colors['border-soft'],\n paddingHorizontal: theme.spacing(4),\n paddingVertical: theme.spacing(3),\n },\n bodyText: {\n color: theme.colors.dim,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n },\n status: {\n color: theme.colors.faint,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n glyph: (tone: StatusTone) => ({ color: theme.colors[STATUS_COLORS[tone].ink] }),\n more: { alignSelf: 'flex-start' },\n}));\n"],"mappings":"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"}
1
+ {"version":3,"file":"MessageItem.mjs","names":["NativeColorToken","createContext","ReactNode","useContext","useMemo","StyleProp","TextProps","ViewProps","ViewStyle","StyleSheet","AlertIcon","CheckIcon","ChevronDownIcon","ChevronRightIcon","WrenchIcon","fontOf","STATUS_COLORS","useControllable","wrapText","Button","Spinner","STATUS_EDGE","StatusBadge","StatusTone","ICON_TONES","ReadonlySet","Set","MIN_ROW_HEIGHT","LEAD_CHIP","ROW_EDGE_WIDTH","MessageItemState","tone","expandable","expanded","toggle","MessageItemContext","undefined","useMessageItem","MessageItemGroupContext","toolTone","state","running","isError","STATUS_LABEL","Readonly","Record","neutral","ok","error","info","accent","STATUS_GLYPH","cardEdge","rowEdge","MessageItemProps","Omit","defaultExpanded","onExpandedChange","children","style","MessageItem","props","current","setExpanded","group","styles","Summary","summary","summaryText","ToolCallLead","title","caption","chip","lead","titleLine","RowLead","Outcome","ink","has","outcomeIcon","outcome","MessageItemGroupProps","badge","MessageItemGroup","label","header","MessageItemHeaderProps","MessageItemHeader","content","pressed","headerPressed","MessageItemPartProps","MessageItemBody","body","bodyText","MessageItemStatusProps","glyph","expands","containerStyle","MessageItemStatus","line","status","more","create","theme","token","overflow","borderRadius","lg","borderWidth","borderColor","colors","uni__dependencies","borderTopWidth","borderTopColor","borderLeftWidth","borderLeftColor","flexDirection","alignItems","gap","spacing","minHeight","paddingHorizontal","paddingVertical","backgroundColor","panel","justifyContent","width","height","flexShrink","full","rail","flex","minWidth","color","dim","fontFamily","sans","fontSize","sm","lineHeight","hi","base","fontWeight","semibold","faint","alignSelf"],"sources":["../../src/components/MessageItem.tsx"],"sourcesContent":["import type { NativeColorToken } from '@agimon-ai/po-web-components/nativeTheme';\nimport { createContext, type ReactNode, useContext, useMemo } from 'react';\nimport { Pressable, type StyleProp, Text, type TextProps, View, type ViewProps, type ViewStyle } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\nimport { AlertIcon, CheckIcon, ChevronDownIcon, ChevronRightIcon, WrenchIcon } from '../icons/icons';\nimport { fontOf } from '../lib/fonts';\nimport { STATUS_COLORS } from '../lib/tone';\nimport { useControllable } from '../lib/useControllable';\nimport { wrapText } from '../lib/wrapText';\nimport { Button } from './Button';\nimport { Spinner } from './Spinner';\nimport { STATUS_EDGE, StatusBadge, type StatusTone } from './StatusBadge';\n\n/** Tones whose outcome reads as an icon alone; the rest keep their worded badge. */\nconst ICON_TONES: ReadonlySet<StatusTone> = new Set<StatusTone>(['running', 'ok', 'error']);\n\n/*\n * The message item: the timeline card a tool call, a run or a notice sits in.\n * The item owns its outcome tone and its expand state; the header, body and\n * status parts read both from it, so a card composes the parts and never\n * threads `expanded` by hand. The header is one Pressable that expands the\n * item, the native stand-in for the web's small chevron button.\n */\n\nconst MIN_ROW_HEIGHT = 44;\nconst LEAD_CHIP = 36;\nconst ROW_EDGE_WIDTH = 2;\n\nexport interface MessageItemState {\n tone: StatusTone;\n /** Whether the header offers the toggle; an item that hides nothing offers none. */\n expandable: boolean;\n expanded: boolean;\n toggle: () => void;\n}\n\nconst MessageItemContext = createContext<MessageItemState>({\n tone: 'neutral',\n expandable: false,\n expanded: false,\n toggle: () => undefined,\n});\n\n/** The enclosing item's state, for a part that lives deeper than the item's render prop. */\nexport function useMessageItem(): MessageItemState {\n return useContext(MessageItemContext);\n}\n\n/** The group an item is a row of, or null for an item that is a card of its own. */\nconst MessageItemGroupContext = createContext<{ tone: StatusTone } | null>(null);\n\n/** The tone a tool call's item wears: running while the tool runs, then its outcome. */\nexport function toolTone(state: { running: boolean; isError: boolean }): StatusTone {\n return state.running ? 'running' : state.isError ? 'error' : 'ok';\n}\n\n/** The badge text a tone reads as when the header is not told otherwise; empty hides the badge. */\nexport const STATUS_LABEL: Readonly<Record<StatusTone, string>> = {\n neutral: '',\n running: 'Running',\n ok: 'Completed',\n error: 'Failed',\n info: 'Info',\n accent: 'Note',\n};\n\n/** The glyph a tone reads as in a status line. */\nexport const STATUS_GLYPH: Readonly<Record<StatusTone, string>> = {\n neutral: '\\u2026',\n running: '\\u25d0',\n ok: '\\u2713',\n error: '\\u2717',\n info: '\\u25cf',\n accent: '\\u25cf',\n};\n\n/** A settled OK is the calm default and keeps the soft border; only work in flight or a failure colours the edge. */\nfunction cardEdge(tone: StatusTone): NativeColorToken {\n return tone === 'ok' ? 'border-soft' : STATUS_EDGE[tone];\n}\n\n/** As a row, a settled or neutral item keeps a quiet edge. */\nfunction rowEdge(tone: StatusTone): NativeColorToken | undefined {\n return tone === 'ok' || tone === 'neutral' ? undefined : STATUS_EDGE[tone];\n}\n\nexport interface MessageItemProps extends Omit<ViewProps, 'children' | 'style'> {\n tone?: StatusTone;\n /** Whether the header offers the expand toggle. A toggle that reveals nothing reads as broken, so the card decides. */\n expandable?: boolean;\n defaultExpanded?: boolean;\n /** Controlled mode; pair with onExpandedChange. Omit it and the item keeps its own state. */\n expanded?: boolean;\n onExpandedChange?: (expanded: boolean) => void;\n /** Plain content, or a function of the item's state for a card that picks its lines by `expanded`. */\n children?: ReactNode | ((state: MessageItemState) => ReactNode);\n style?: StyleProp<ViewStyle>;\n}\n\nexport function MessageItem({\n tone = 'neutral',\n expandable = false,\n defaultExpanded = false,\n expanded,\n onExpandedChange,\n children,\n style,\n ...props\n}: MessageItemProps) {\n const [current, setExpanded] = useControllable(expanded, defaultExpanded, onExpandedChange);\n const group = useContext(MessageItemGroupContext);\n const state: MessageItemState = { tone, expandable, expanded: current, toggle: () => setExpanded(!current) };\n return (\n <MessageItemContext.Provider value={state}>\n <View\n style={[group === null ? styles.cardEdge(cardEdge(tone)) : styles.rowEdge(rowEdge(tone)), style]}\n {...props}\n >\n {typeof children === 'function' ? children(state) : children}\n </View>\n </MessageItemContext.Provider>\n );\n}\n\n/** The line beside the title; a bare string stays on one line and truncates, as the web's `truncate`. */\nfunction Summary({ children }: { children?: ReactNode }) {\n return (\n <View style={styles.summary}>\n {typeof children === 'string' ? (\n <Text numberOfLines={1} style={styles.summaryText}>\n {children}\n </Text>\n ) : (\n wrapText(children, styles.summaryText)\n )}\n </View>\n );\n}\n\n/** The card's lead: the tool chip, the \"Tool call\" caption, then the name and its summary on one line. */\nfunction ToolCallLead({\n title,\n caption = 'Tool call',\n children,\n}: {\n title: ReactNode;\n caption?: string | null;\n children?: ReactNode;\n}) {\n return (\n <>\n <View accessible={false} style={styles.chip}>\n <WrenchIcon size={16} />\n </View>\n <View style={styles.lead}>\n {caption === null ? null : <Text style={styles.caption}>{caption}</Text>}\n <View style={styles.titleLine}>\n {wrapText(title, styles.title)}\n <Summary>{children}</Summary>\n </View>\n </View>\n </>\n );\n}\n\n/** A row's lead inside a group: the name and its summary, no chip or caption, since the group header carries them. */\nfunction RowLead({ title, children }: { title: ReactNode; children?: ReactNode }) {\n return (\n <View style={[styles.lead, styles.titleLine]}>\n {wrapText(title, styles.title)}\n <Summary>{children}</Summary>\n </View>\n );\n}\n\n/** The outcome: an icon for running, done and failed, announced by its label; a worded badge for other tones. */\nfunction Outcome({ tone, children }: { tone: StatusTone; children: ReactNode }) {\n const ink = STATUS_COLORS[tone].ink;\n if (ICON_TONES.has(tone)) {\n return (\n <View\n accessible\n accessibilityRole=\"image\"\n accessibilityLabel={typeof children === 'string' ? children : STATUS_LABEL[tone]}\n style={styles.outcomeIcon}\n >\n {tone === 'running' ? <Spinner color={ink} /> : null}\n {tone === 'ok' ? <CheckIcon size={14} color={ink} /> : null}\n {tone === 'error' ? <AlertIcon size={14} color={ink} /> : null}\n </View>\n );\n }\n return (\n <StatusBadge tone={tone} size=\"md\" style={styles.outcome}>\n {children}\n </StatusBadge>\n );\n}\n\nexport interface MessageItemGroupProps extends Omit<ViewProps, 'children' | 'style'> {\n tone?: StatusTone;\n /** The bold label at the left of the group header, such as \"7 tool calls\". */\n title: ReactNode;\n /** The line beside it, such as which tools the group holds. */\n summary?: ReactNode;\n /** The outcome's label; defaults to the tone's label, null hides the outcome. */\n badge?: ReactNode | null;\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * One frame around a run of items that belong together. It stays open: each\n * item inside becomes a row with its own name, outcome and expand, no card of\n * its own, and its tone an edge down its left side.\n */\nexport function MessageItemGroup({\n tone = 'neutral',\n title,\n summary,\n badge,\n children,\n style,\n ...props\n}: MessageItemGroupProps) {\n const label = badge === undefined ? STATUS_LABEL[tone] : badge;\n // A stable value, so a group re-render does not re-render every memoised row's item.\n const group = useMemo(() => ({ tone }), [tone]);\n return (\n <MessageItem tone={tone} style={style} {...props}>\n <View style={styles.header}>\n <ToolCallLead title={title} caption={null}>\n {summary}\n </ToolCallLead>\n {label !== null && label !== '' ? <Outcome tone={tone}>{label}</Outcome> : null}\n </View>\n <MessageItemGroupContext.Provider value={group}>\n <View>{children}</View>\n </MessageItemGroupContext.Provider>\n </MessageItem>\n );\n}\n\nexport interface MessageItemHeaderProps extends Omit<ViewProps, 'children' | 'style'> {\n /** The bold label at the left: the tool name. */\n title: ReactNode;\n /** The badge text; defaults to the tone's label, null hides the badge. */\n badge?: ReactNode | null;\n /** The summary beside the title; it gets the remaining width. */\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * The title row: label, summary, the tone's outcome and, when the item is\n * expandable, a chevron; the whole row is the toggle. A row inside a group\n * keeps its name but drops the chip and caption the group header carries.\n */\nexport function MessageItemHeader({ title, badge, children, style, ...props }: MessageItemHeaderProps) {\n const { tone, expandable, expanded, toggle } = useMessageItem();\n const group = useContext(MessageItemGroupContext);\n const label = badge === undefined ? STATUS_LABEL[tone] : badge;\n const content = (\n <>\n {group === null ? (\n <ToolCallLead title={title}>{children}</ToolCallLead>\n ) : (\n <RowLead title={title}>{children}</RowLead>\n )}\n {label !== null && label !== '' ? <Outcome tone={tone}>{label}</Outcome> : null}\n {expandable ? expanded ? <ChevronDownIcon size={16} /> : <ChevronRightIcon size={16} /> : null}\n </>\n );\n if (!expandable) {\n return (\n <View style={[styles.header, style]} {...props}>\n {content}\n </View>\n );\n }\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ expanded }}\n accessibilityHint={expanded ? 'collapse' : 'expand'}\n onPress={toggle}\n style={({ pressed }) => [styles.header, pressed && styles.headerPressed, style]}\n {...props}\n >\n {content}\n </Pressable>\n );\n}\n\nexport interface MessageItemPartProps extends Omit<ViewProps, 'children' | 'style'> {\n children?: ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\n/** The body under the header, on the card's own surface. */\nexport function MessageItemBody({ children, style, ...props }: MessageItemPartProps) {\n return (\n <View style={[styles.body, style]} {...props}>\n {wrapText(children, styles.bodyText)}\n </View>\n );\n}\n\nexport interface MessageItemStatusProps extends Omit<TextProps, 'children'> {\n tone?: StatusTone;\n /** Replaces the tone's glyph. */\n glyph?: string;\n /** Renders as a button that expands the item: the \"N more line(s)\" hint. */\n expands?: boolean;\n children?: ReactNode;\n /** Applied to the row; with `expands`, to the button. */\n containerStyle?: StyleProp<ViewStyle>;\n}\n\n/** The one-line status under a body: `running`, `failed`, `done`, or the hint that more is hidden. */\nexport function MessageItemStatus({\n tone = 'neutral',\n glyph,\n expands = false,\n children,\n containerStyle,\n style,\n ...props\n}: MessageItemStatusProps) {\n const { toggle } = useMessageItem();\n const line = (\n <Text style={[styles.status, style]} {...props}>\n <Text style={styles.glyph(tone)}>{glyph ?? STATUS_GLYPH[tone]}</Text> {children}\n </Text>\n );\n if (expands) {\n return (\n <Button variant=\"ghost\" size=\"xs\" onPress={toggle} style={[styles.more, containerStyle]}>\n {line}\n </Button>\n );\n }\n return <View style={containerStyle}>{line}</View>;\n}\n\nconst styles = StyleSheet.create((theme) => ({\n cardEdge: (token: NativeColorToken) => ({\n overflow: 'hidden',\n borderRadius: theme.borderRadius.lg,\n borderWidth: 1,\n borderColor: theme.colors[token],\n }),\n // A row's top rule is the group's separator; its left edge carries the tone.\n rowEdge: (token: NativeColorToken | undefined) => ({\n overflow: 'hidden',\n borderTopWidth: 1,\n borderTopColor: theme.colors['border-soft'],\n borderLeftWidth: ROW_EDGE_WIDTH,\n borderLeftColor: token === undefined ? 'transparent' : theme.colors[token],\n }),\n header: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.spacing(3),\n minHeight: MIN_ROW_HEIGHT,\n paddingHorizontal: theme.spacing(4),\n paddingVertical: theme.spacing(3),\n },\n headerPressed: { backgroundColor: theme.colors.panel },\n chip: {\n alignItems: 'center',\n justifyContent: 'center',\n width: LEAD_CHIP,\n height: LEAD_CHIP,\n flexShrink: 0,\n borderRadius: theme.borderRadius.full,\n backgroundColor: theme.colors.rail,\n },\n lead: { flex: 1, minWidth: 0 },\n caption: {\n color: theme.colors.dim,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n titleLine: { flexDirection: 'row', alignItems: 'center', gap: theme.spacing(2), minWidth: 0 },\n title: {\n flexShrink: 0,\n color: theme.colors.hi,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n fontWeight: theme.fontWeight.semibold,\n },\n summary: { flex: 1, flexDirection: 'row', alignItems: 'center', gap: theme.spacing(2), minWidth: 0 },\n summaryText: {\n flexShrink: 1,\n color: theme.colors.faint,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n },\n outcome: { gap: theme.spacing(1) },\n outcomeIcon: { alignItems: 'center', justifyContent: 'center', width: 20, height: 20, flexShrink: 0 },\n body: {\n gap: theme.spacing(1),\n borderTopWidth: 1,\n borderTopColor: theme.colors['border-soft'],\n paddingHorizontal: theme.spacing(4),\n paddingVertical: theme.spacing(3),\n },\n bodyText: {\n color: theme.colors.dim,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.base,\n lineHeight: theme.lineHeight.base,\n },\n status: {\n color: theme.colors.faint,\n fontFamily: fontOf(theme.fontFamily.sans),\n fontSize: theme.fontSize.sm,\n lineHeight: theme.lineHeight.sm,\n },\n glyph: (tone: StatusTone) => ({ color: theme.colors[STATUS_COLORS[tone].ink] }),\n more: { alignSelf: 'flex-start' },\n}));\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"SyntaxLine.d.mts","names":[],"sources":["../../src/components/SyntaxLine.tsx"],"mappings":";;;;;;;wBAgBgB,eAAe,cAAc;EAAS;uBAAgC;iBAuBrE,wBAAwB,KAAK;;EAE5C,iBAAiB;EACjB;;;wBAIc,aAAa,OAAO,SAAS,SAAS,kCAAe,IAAA"}
1
+ {"version":3,"file":"SyntaxLine.d.mts","names":[],"sources":["../../src/components/SyntaxLine.tsx"],"mappings":";;;;;;;wBAgBgB,eAAe,cAAc;EAAS;uBAAgC;iBAyBrE,wBAAwB,KAAK;;EAE5C,iBAAiB;EACjB;;;wBAIc,aAAa,OAAO,SAAS,SAAS,kCAAe,IAAA"}
@@ -19,7 +19,8 @@ function useSyntaxLines(text, query = {}) {
19
19
  useEffect(() => {
20
20
  if (grammar === void 0) return;
21
21
  let live = true;
22
- highlightToLines(text, grammar).then((lines) => {
22
+ const controller = new AbortController();
23
+ highlightToLines(text, grammar, controller.signal).then((lines) => {
23
24
  if (live && lines !== void 0) setDone({
24
25
  signature,
25
26
  lines
@@ -27,6 +28,7 @@ function useSyntaxLines(text, query = {}) {
27
28
  }, () => void 0);
28
29
  return () => {
29
30
  live = false;
31
+ controller.abort();
30
32
  };
31
33
  }, [
32
34
  text,
@@ -1 +1 @@
1
- {"version":3,"file":"SyntaxLine.mjs","names":["detectGrammar","highlightToLines","SyntaxSpan","useEffect","useState","TextProps","StyleSheet","useSyntaxLines","text","query","path","grammar","signature","done","setDone","lines","undefined","live","then","SyntaxLineProps","Omit","spans","SyntaxLine","props","map","span","index","token","syntax","create","theme","comment","color","colors","faint","fontStyle","uni__dependencies","keyword","magenta","constant","violet","literal","orange","string","green","regexp","teal","operator","blue","punctuation","dim","variable","property","cyan","callable","type","yellow","tag","red","attribute","meta","heading","hi","fontWeight","link","textDecorationLine","emphasis","strong","strikethrough","invalid"],"sources":["../../src/components/SyntaxLine.tsx"],"sourcesContent":["import { detectGrammar, highlightToLines, type SyntaxSpan } from '@agimon-ai/po-web-components/text';\nimport { useEffect, useState } from 'react';\nimport { Text, type TextProps } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\n/*\n * Read-only code with the editor's colours, as the web's SyntaxText: the same\n * lezer grammars through `@agimon-ai/po-web-components/text`, and the web's\n * `PO_SYNTAX_STYLES` palette on the native theme. Highlighting is async, so the\n * plain text shows first and a result that never highlights stays plain.\n */\n\n/**\n * Colours `text` as lines of spans, or undefined while loading and for text with\n * no grammar. Pass a whole block, not one line: a line is not a parse unit.\n */\nexport function useSyntaxLines(text: string, query: { path?: string | undefined } = {}) {\n const grammar = detectGrammar({ path: query.path, text });\n const signature = `${grammar ?? ''}\\u0000${text}`;\n const [done, setDone] = useState<{ signature: string; lines: readonly (readonly SyntaxSpan[])[] }>();\n\n useEffect(() => {\n if (grammar === undefined) return;\n let live = true;\n void highlightToLines(text, grammar).then(\n (lines) => {\n if (live && lines !== undefined) setDone({ signature, lines });\n },\n // A grammar that fails to load leaves the plain text in place: colour is decoration.\n () => undefined,\n );\n return () => {\n live = false;\n };\n }, [text, grammar, signature]);\n\n return done?.signature === signature ? done.lines : undefined;\n}\n\nexport interface SyntaxLineProps extends Omit<TextProps, 'children'> {\n /** The line's spans from `useSyntaxLines`, or undefined to show `text` plain. */\n spans?: readonly SyntaxSpan[] | undefined;\n text: string;\n}\n\n/** One line: coloured when its spans are in, the plain text until then. */\nexport function SyntaxLine({ spans, text, ...props }: SyntaxLineProps) {\n return (\n <Text {...props}>\n {spans?.map((span, index) => (\n <Text key={index} style={span.token === undefined ? undefined : syntax[span.token]}>\n {span.text}\n </Text>\n )) ?? text}\n </Text>\n );\n}\n\n/** The web's `PO_SYNTAX_STYLES`, on the native theme's colours. */\nconst syntax = StyleSheet.create((theme) => ({\n comment: { color: theme.colors.faint, fontStyle: 'italic' },\n keyword: { color: theme.colors.magenta },\n constant: { color: theme.colors.violet },\n literal: { color: theme.colors.orange },\n string: { color: theme.colors.green },\n regexp: { color: theme.colors.teal },\n operator: { color: theme.colors.blue },\n punctuation: { color: theme.colors.dim },\n variable: { color: theme.colors.text },\n property: { color: theme.colors.cyan },\n callable: { color: theme.colors.blue },\n type: { color: theme.colors.yellow },\n tag: { color: theme.colors.red },\n attribute: { color: theme.colors.yellow },\n meta: { color: theme.colors.faint },\n heading: { color: theme.colors.hi, fontWeight: 'bold' },\n link: { color: theme.colors.cyan, textDecorationLine: 'underline' },\n emphasis: { fontStyle: 'italic' },\n strong: { fontWeight: 'bold' },\n strikethrough: { textDecorationLine: 'line-through' },\n invalid: { color: theme.colors.red },\n}));\n"],"mappings":"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"}
1
+ {"version":3,"file":"SyntaxLine.mjs","names":["detectGrammar","highlightToLines","SyntaxSpan","useEffect","useState","TextProps","StyleSheet","useSyntaxLines","text","query","path","grammar","signature","done","setDone","lines","undefined","live","controller","AbortController","signal","then","abort","SyntaxLineProps","Omit","spans","SyntaxLine","props","map","span","index","token","syntax","create","theme","comment","color","colors","faint","fontStyle","uni__dependencies","keyword","magenta","constant","violet","literal","orange","string","green","regexp","teal","operator","blue","punctuation","dim","variable","property","cyan","callable","type","yellow","tag","red","attribute","meta","heading","hi","fontWeight","link","textDecorationLine","emphasis","strong","strikethrough","invalid"],"sources":["../../src/components/SyntaxLine.tsx"],"sourcesContent":["import { detectGrammar, highlightToLines, type SyntaxSpan } from '@agimon-ai/po-web-components/text';\nimport { useEffect, useState } from 'react';\nimport { Text, type TextProps } from 'react-native';\nimport { StyleSheet } from 'react-native-unistyles';\n\n/*\n * Read-only code with the editor's colours, as the web's SyntaxText: the same\n * lezer grammars through `@agimon-ai/po-web-components/text`, and the web's\n * `PO_SYNTAX_STYLES` palette on the native theme. Highlighting is async, so the\n * plain text shows first and a result that never highlights stays plain.\n */\n\n/**\n * Colours `text` as lines of spans, or undefined while loading and for text with\n * no grammar. Pass a whole block, not one line: a line is not a parse unit.\n */\nexport function useSyntaxLines(text: string, query: { path?: string | undefined } = {}) {\n const grammar = detectGrammar({ path: query.path, text });\n const signature = `${grammar ?? ''}\\u0000${text}`;\n const [done, setDone] = useState<{ signature: string; lines: readonly (readonly SyntaxSpan[])[] }>();\n\n useEffect(() => {\n if (grammar === undefined) return;\n let live = true;\n const controller = new AbortController();\n void highlightToLines(text, grammar, controller.signal).then(\n (lines) => {\n if (live && lines !== undefined) setDone({ signature, lines });\n },\n // A grammar that fails to load leaves the plain text in place: colour is decoration.\n () => undefined,\n );\n return () => {\n live = false;\n controller.abort();\n };\n }, [text, grammar, signature]);\n\n return done?.signature === signature ? done.lines : undefined;\n}\n\nexport interface SyntaxLineProps extends Omit<TextProps, 'children'> {\n /** The line's spans from `useSyntaxLines`, or undefined to show `text` plain. */\n spans?: readonly SyntaxSpan[] | undefined;\n text: string;\n}\n\n/** One line: coloured when its spans are in, the plain text until then. */\nexport function SyntaxLine({ spans, text, ...props }: SyntaxLineProps) {\n return (\n <Text {...props}>\n {spans?.map((span, index) => (\n <Text key={index} style={span.token === undefined ? undefined : syntax[span.token]}>\n {span.text}\n </Text>\n )) ?? text}\n </Text>\n );\n}\n\n/** The web's `PO_SYNTAX_STYLES`, on the native theme's colours. */\nconst syntax = StyleSheet.create((theme) => ({\n comment: { color: theme.colors.faint, fontStyle: 'italic' },\n keyword: { color: theme.colors.magenta },\n constant: { color: theme.colors.violet },\n literal: { color: theme.colors.orange },\n string: { color: theme.colors.green },\n regexp: { color: theme.colors.teal },\n operator: { color: theme.colors.blue },\n punctuation: { color: theme.colors.dim },\n variable: { color: theme.colors.text },\n property: { color: theme.colors.cyan },\n callable: { color: theme.colors.blue },\n type: { color: theme.colors.yellow },\n tag: { color: theme.colors.red },\n attribute: { color: theme.colors.yellow },\n meta: { color: theme.colors.faint },\n heading: { color: theme.colors.hi, fontWeight: 'bold' },\n link: { color: theme.colors.cyan, textDecorationLine: 'underline' },\n emphasis: { fontStyle: 'italic' },\n strong: { fontWeight: 'bold' },\n strikethrough: { textDecorationLine: 'line-through' },\n invalid: { color: theme.colors.red },\n}));\n"],"mappings":"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"}
@@ -15,20 +15,24 @@ const HTML_TAG_LINE = /^ {0,3}<\/?[A-Za-z][A-Za-z0-9-]*(?:\s[^<>]*)?\/?>[ \t]*$/
15
15
  const HARD_BREAK = /(?: {2,}|\\)$/;
16
16
  const ESCAPABLE = /[!-/:-@[-`{-~]/;
17
17
  const WORD_CHAR = /[\p{L}\p{N}]/u;
18
+ const MAX_PARSE_CHARACTERS = 1e5;
19
+ const MAX_PARSE_WORK = 25e4;
20
+ const MAX_PARSE_DEPTH = 32;
21
+ const PARSE_LIMIT = /* @__PURE__ */ new Error("Markdown parsing limit reached.");
22
+ function spend(budget, work) {
23
+ budget.remaining -= work;
24
+ if (budget.remaining < 0) throw PARSE_LIMIT;
25
+ }
18
26
  function isBlank(line) {
19
27
  return line.trim() === "";
20
28
  }
21
29
  /** Joins a paragraph's lines: a space between them, or a line break after two spaces or a backslash. */
22
30
  function joinLines(lines) {
23
- let joined = "";
24
- lines.forEach((line, index) => {
25
- if (index === 0) joined = line;
26
- else {
27
- const hard = HARD_BREAK.test(joined);
28
- joined = `${joined.replace(/(?: {2,}|\\|[ \t]+)$/u, "")}${hard ? "\n" : " "}${line.trimStart()}`;
29
- }
30
- });
31
- return joined;
31
+ return lines.map((line, index) => {
32
+ const current = index === 0 ? line : line.trimStart();
33
+ if (index === lines.length - 1) return current;
34
+ return `${current.replace(/(?: {2,}|\\|[ \t]+)$/u, "")}${HARD_BREAK.test(current) ? "\n" : " "}`;
35
+ }).join("");
32
36
  }
33
37
  /** A table row's cells: outer pipes dropped, split on unescaped pipes, `\|` read as a pipe. */
34
38
  function tableCells(line) {
@@ -52,6 +56,23 @@ function tableCells(line) {
52
56
  }
53
57
  /** Parses a Markdown document into blocks. */
54
58
  function parseMarkdown(source) {
59
+ if (source.length <= MAX_PARSE_CHARACTERS) try {
60
+ return parseBlocks(source, { remaining: MAX_PARSE_WORK }, 0);
61
+ } catch (error) {
62
+ if (error !== PARSE_LIMIT) throw error;
63
+ }
64
+ return [{
65
+ type: "paragraph",
66
+ children: [{
67
+ type: "text",
68
+ text: source
69
+ }]
70
+ }];
71
+ }
72
+ function parseBlocks(source, budget, depth) {
73
+ if (depth > MAX_PARSE_DEPTH) throw PARSE_LIMIT;
74
+ spend(budget, source.length + 1);
75
+ const inline = (text) => parseInlineNodes(text, budget, depth + 1);
55
76
  const lines = source.replace(/\r\n?/g, "\n").split("\n");
56
77
  const blocks = [];
57
78
  let paragraph = [];
@@ -59,7 +80,7 @@ function parseMarkdown(source) {
59
80
  const flushParagraph = () => {
60
81
  if (paragraph.length > 0) blocks.push({
61
82
  type: "paragraph",
62
- children: parseInline(joinLines(paragraph))
83
+ children: inline(joinLines(paragraph))
63
84
  });
64
85
  paragraph = [];
65
86
  };
@@ -68,7 +89,7 @@ function parseMarkdown(source) {
68
89
  type: "list",
69
90
  ordered: list.ordered,
70
91
  start: list.start,
71
- items: list.items.map((item) => parseInline(joinLines(item)))
92
+ items: list.items.map((item) => inline(joinLines(item)))
72
93
  });
73
94
  list = void 0;
74
95
  };
@@ -118,7 +139,7 @@ function parseMarkdown(source) {
118
139
  blocks.push({
119
140
  type: "heading",
120
141
  level,
121
- children: parseInline(heading[2] ?? "")
142
+ children: inline(heading[2] ?? "")
122
143
  });
123
144
  continue;
124
145
  }
@@ -137,7 +158,7 @@ function parseMarkdown(source) {
137
158
  index -= 1;
138
159
  blocks.push({
139
160
  type: "quote",
140
- children: parseMarkdown(quoted.join("\n"))
161
+ children: parseBlocks(quoted.join("\n"), budget, depth + 1)
141
162
  });
142
163
  continue;
143
164
  }
@@ -150,13 +171,13 @@ function parseMarkdown(source) {
150
171
  const rows = [];
151
172
  while (index < lines.length && !isBlank(lines[index]) && lines[index].includes("|")) {
152
173
  const cells = tableCells(lines[index]);
153
- rows.push(header.map((_, column) => parseInline(cells[column] ?? "")));
174
+ rows.push(header.map((_, column) => inline(cells[column] ?? "")));
154
175
  index += 1;
155
176
  }
156
177
  index -= 1;
157
178
  blocks.push({
158
179
  type: "table",
159
- header: header.map((cell) => parseInline(cell)),
180
+ header: header.map((cell) => inline(cell)),
160
181
  rows
161
182
  });
162
183
  continue;
@@ -188,8 +209,9 @@ function parseMarkdown(source) {
188
209
  flush();
189
210
  return blocks;
190
211
  }
191
- /** Parses one run of inline Markdown. */
192
- function parseInline(source) {
212
+ function parseInlineNodes(source, budget, depth) {
213
+ if (depth > MAX_PARSE_DEPTH) throw PARSE_LIMIT;
214
+ spend(budget, source.length + 1);
193
215
  const nodes = [];
194
216
  let text = "";
195
217
  const pushText = (value) => {
@@ -205,6 +227,7 @@ function parseInline(source) {
205
227
  };
206
228
  let index = 0;
207
229
  while (index < source.length) {
230
+ spend(budget, 1);
208
231
  const char = source[index];
209
232
  if (char === "\\" && index + 1 < source.length && ESCAPABLE.test(source[index + 1])) {
210
233
  pushText(source[index + 1]);
@@ -212,7 +235,7 @@ function parseInline(source) {
212
235
  continue;
213
236
  }
214
237
  if (char === "`") {
215
- const span = readCodeSpan(source, index);
238
+ const span = readCodeSpan(source, index, budget);
216
239
  if (span !== void 0) {
217
240
  pushNode({
218
241
  type: "code",
@@ -227,7 +250,7 @@ function parseInline(source) {
227
250
  continue;
228
251
  }
229
252
  if (char === "!" && source[index + 1] === "[") {
230
- const image = readLink(source, index + 1);
253
+ const image = readLink(source, index + 1, budget);
231
254
  if (image !== void 0) {
232
255
  pushNode({
233
256
  type: "image",
@@ -239,23 +262,23 @@ function parseInline(source) {
239
262
  }
240
263
  }
241
264
  if (char === "[") {
242
- const link = readLink(source, index);
265
+ const link = readLink(source, index, budget);
243
266
  if (link !== void 0) {
244
267
  pushNode({
245
268
  type: "link",
246
269
  href: link.href,
247
- children: parseInline(link.label)
270
+ children: parseInlineNodes(link.label, budget, depth + 1)
248
271
  });
249
272
  index = link.end;
250
273
  continue;
251
274
  }
252
275
  }
253
276
  if ((char === "*" || char === "_") && source[index + 1] === char) {
254
- const strong = readDelimited(source, index, char.repeat(2));
277
+ const strong = readDelimited(source, index, char.repeat(2), budget);
255
278
  if (strong !== void 0) {
256
279
  pushNode({
257
280
  type: "strong",
258
- children: parseInline(strong.inner)
281
+ children: parseInlineNodes(strong.inner, budget, depth + 1)
259
282
  });
260
283
  index = strong.end;
261
284
  continue;
@@ -265,11 +288,11 @@ function parseInline(source) {
265
288
  continue;
266
289
  }
267
290
  if (char === "*" || char === "_") {
268
- const em = readDelimited(source, index, char);
291
+ const em = readDelimited(source, index, char, budget);
269
292
  if (em !== void 0) {
270
293
  pushNode({
271
294
  type: "em",
272
- children: parseInline(em.inner)
295
+ children: parseInlineNodes(em.inner, budget, depth + 1)
273
296
  });
274
297
  index = em.end;
275
298
  continue;
@@ -285,11 +308,12 @@ function parseInline(source) {
285
308
  return nodes;
286
309
  }
287
310
  /** A code span opened by the backtick run at `start`, closed by a run of the same length. */
288
- function readCodeSpan(source, start) {
311
+ function readCodeSpan(source, start, budget) {
289
312
  const run = /^`+/.exec(source.slice(start))[0];
290
313
  let search = start + run.length;
291
314
  while (search < source.length) {
292
315
  const found = source.indexOf(run, search);
316
+ spend(budget, (found === -1 ? source.length : found + run.length) - search);
293
317
  if (found === -1) return void 0;
294
318
  const after = found + run.length;
295
319
  if (source[after] === "`" || source[found - 1] === "`") {
@@ -306,10 +330,11 @@ function readCodeSpan(source, start) {
306
330
  }
307
331
  }
308
332
  /** `[label](href)` starting at the bracket; brackets in the label may nest. */
309
- function readLink(source, start) {
333
+ function readLink(source, start, budget) {
310
334
  let depth = 0;
311
335
  let close = -1;
312
336
  for (let index = start; index < source.length; index += 1) {
337
+ spend(budget, 1);
313
338
  const char = source[index];
314
339
  if (char === "\\") {
315
340
  index += 1;
@@ -325,12 +350,39 @@ function readLink(source, start) {
325
350
  }
326
351
  }
327
352
  if (close === -1 || source[close + 1] !== "(") return void 0;
328
- const target = /^\(\s*<?([^\s()<>]*)>?(?:\s+"[^"]*")?\s*\)/.exec(source.slice(close + 1));
329
- if (target === null) return void 0;
353
+ let index = close + 2;
354
+ const skipSpace = () => {
355
+ while (index < source.length && /\s/.test(source[index])) {
356
+ spend(budget, 1);
357
+ index += 1;
358
+ }
359
+ };
360
+ skipSpace();
361
+ if (source[index] === "<") index += 1;
362
+ const hrefStart = index;
363
+ while (index < source.length && !/[\s()<>]/.test(source[index])) {
364
+ spend(budget, 1);
365
+ index += 1;
366
+ }
367
+ const href = source.slice(hrefStart, index);
368
+ if (source[index] === ">") index += 1;
369
+ const beforeSpace = index;
370
+ skipSpace();
371
+ if (index > beforeSpace && source[index] === "\"") {
372
+ index += 1;
373
+ while (index < source.length && source[index] !== "\"") {
374
+ spend(budget, 1);
375
+ index += 1;
376
+ }
377
+ if (source[index] !== "\"") return void 0;
378
+ index += 1;
379
+ skipSpace();
380
+ }
381
+ if (source[index] !== ")") return void 0;
330
382
  return {
331
383
  label: source.slice(start + 1, close),
332
- href: target[1],
333
- end: close + 1 + target[0].length
384
+ href,
385
+ end: index + 1
334
386
  };
335
387
  }
336
388
  /**
@@ -338,7 +390,7 @@ function readLink(source, start) {
338
390
  * end with a space, and an underscore only counts at a word boundary, so
339
391
  * `snake_case_name` stays text.
340
392
  */
341
- function readDelimited(source, start, delimiter) {
393
+ function readDelimited(source, start, delimiter, budget) {
342
394
  const underscore = delimiter[0] === "_";
343
395
  if (underscore && start > 0 && WORD_CHAR.test(source[start - 1])) return void 0;
344
396
  const open = start + delimiter.length;
@@ -346,6 +398,7 @@ function readDelimited(source, start, delimiter) {
346
398
  let search = open;
347
399
  while (search < source.length) {
348
400
  const found = source.indexOf(delimiter, search);
401
+ spend(budget, (found === -1 ? source.length : found + delimiter.length) - search);
349
402
  if (found === -1) return void 0;
350
403
  const end = found + delimiter.length;
351
404
  if (delimiter.length === 1 && source[end] === delimiter) {
@@ -363,5 +416,5 @@ function readDelimited(source, start, delimiter) {
363
416
  }
364
417
 
365
418
  //#endregion
366
- export { parseInline, parseMarkdown };
419
+ export { parseMarkdown };
367
420
  //# sourceMappingURL=markdown.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.mjs","names":[],"sources":["../../src/lib/markdown.ts"],"sourcesContent":["/*\n * A small Markdown parser for native message text: the subset an assistant\n * reply uses, parsed to plain data so the renderer and the tests share one\n * shape. Kept free of React Native so a unit test can run it in Node.\n *\n * Blocks: ATX headings, paragraphs, bullet and numbered lists, fenced code\n * (an unclosed fence runs to the end, so a streaming reply shows its code\n * before the closing fence arrives), thematic breaks, blockquotes and GFM\n * tables. HTML blocks are dropped, as the web renderer drops raw HTML.\n * Inline: code spans, bold, italic, links, images and backslash escapes.\n * A line break inside a paragraph is a space unless the line ends in two\n * spaces or a backslash, as CommonMark reads it.\n *\n * ponytail: no nested lists, setext headings or lazy quote continuation yet;\n * their source falls through as paragraph text, so nothing is lost.\n */\n\nexport type MarkdownInline =\n | { type: 'text'; text: string }\n | { type: 'code'; text: string }\n | { type: 'strong'; children: MarkdownInline[] }\n | { type: 'em'; children: MarkdownInline[] }\n | { type: 'link'; href: string; children: MarkdownInline[] }\n | { type: 'image'; src: string; alt: string };\n\nexport type MarkdownHeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\n\nexport type MarkdownBlock =\n | { type: 'heading'; level: MarkdownHeadingLevel; children: MarkdownInline[] }\n | { type: 'paragraph'; children: MarkdownInline[] }\n | { type: 'list'; ordered: boolean; start: number; items: MarkdownInline[][] }\n | { type: 'code'; language: string | undefined; text: string }\n | { type: 'rule' }\n | { type: 'quote'; children: MarkdownBlock[] }\n | { type: 'table'; header: MarkdownInline[][]; rows: MarkdownInline[][][] };\n\nconst FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})[ \\t]*([^\\s`]*)/;\nconst HEADING = /^ {0,3}(#{1,6})(?:[ \\t]+(.*?))?(?:[ \\t]+#+)?[ \\t]*$/;\nconst RULE = /^ {0,3}([-*_])(?:[ \\t]*\\1){2,}[ \\t]*$/;\nconst BULLET_ITEM = /^ {0,3}[-*+][ \\t]+(.*)$/;\nconst ORDERED_ITEM = /^ {0,3}(\\d{1,9})[.)][ \\t]+(.*)$/;\nconst INDENTED = /^[ \\t]+\\S/;\nconst QUOTE = /^ {0,3}> ?(.*)$/;\nconst TABLE_DIVIDER = /^ {0,3}\\|?[ \\t]*:?-+:?[ \\t]*(?:\\|[ \\t]*:?-+:?[ \\t]*)*\\|?[ \\t]*$/;\nconst HTML_COMMENT = /^ {0,3}<!--/;\n/** CommonMark's block-level tag names, plus the picture and image markup READMEs open with. */\nconst HTML_BLOCK =\n /^ {0,3}<\\/?(?:address|article|aside|blockquote|center|details|div|dl|figcaption|figure|footer|form|h[1-6]|header|hr|li|main|nav|ol|p|picture|section|summary|table|tbody|td|tfoot|th|thead|tr|ul)(?=[\\s/>]|$)/i;\n/** Any other tag standing alone on its line, such as `<img ...>` or `<br>`. */\nconst HTML_TAG_LINE = /^ {0,3}<\\/?[A-Za-z][A-Za-z0-9-]*(?:\\s[^<>]*)?\\/?>[ \\t]*$/;\nconst HARD_BREAK = /(?: {2,}|\\\\)$/;\nconst ESCAPABLE = /[!-/:-@[-`{-~]/;\nconst WORD_CHAR = /[\\p{L}\\p{N}]/u;\n\nfunction isBlank(line: string): boolean {\n return line.trim() === '';\n}\n\n/** Joins a paragraph's lines: a space between them, or a line break after two spaces or a backslash. */\nfunction joinLines(lines: readonly string[]): string {\n let joined = '';\n lines.forEach((line, index) => {\n if (index === 0) joined = line;\n else {\n const hard = HARD_BREAK.test(joined);\n joined = `${joined.replace(/(?: {2,}|\\\\|[ \\t]+)$/u, '')}${hard ? '\\n' : ' '}${line.trimStart()}`;\n }\n });\n return joined;\n}\n\n/** A table row's cells: outer pipes dropped, split on unescaped pipes, `\\|` read as a pipe. */\nfunction tableCells(line: string): string[] {\n let row = line.trim();\n if (row.startsWith('|')) row = row.slice(1);\n if (row.endsWith('|') && !row.endsWith('\\\\|')) row = row.slice(0, -1);\n const cells: string[] = [];\n let cell = '';\n for (let index = 0; index < row.length; index += 1) {\n const char = row[index]!;\n if (char === '\\\\' && row[index + 1] === '|') {\n cell += '|';\n index += 1;\n } else if (char === '|') {\n cells.push(cell.trim());\n cell = '';\n } else cell += char;\n }\n cells.push(cell.trim());\n return cells;\n}\n\n/** Parses a Markdown document into blocks. */\nexport function parseMarkdown(source: string): MarkdownBlock[] {\n const lines = source.replace(/\\r\\n?/g, '\\n').split('\\n');\n const blocks: MarkdownBlock[] = [];\n let paragraph: string[] = [];\n let list: { ordered: boolean; start: number; items: string[][] } | undefined;\n\n const flushParagraph = () => {\n if (paragraph.length > 0) blocks.push({ type: 'paragraph', children: parseInline(joinLines(paragraph)) });\n paragraph = [];\n };\n const flushList = () => {\n if (list !== undefined)\n blocks.push({\n type: 'list',\n ordered: list.ordered,\n start: list.start,\n items: list.items.map((item) => parseInline(joinLines(item))),\n });\n list = undefined;\n };\n const flush = () => {\n flushParagraph();\n flushList();\n };\n\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index]!;\n\n const fence = FENCE_OPEN.exec(line);\n if (fence !== null) {\n flush();\n const marker = fence[1]!;\n const close = new RegExp(`^ {0,3}${marker[0] === '`' ? '`' : '~'}{${marker.length},}[ \\\\t]*$`);\n const body: string[] = [];\n index += 1;\n while (index < lines.length && !close.test(lines[index]!)) {\n body.push(lines[index]!);\n index += 1;\n }\n const language = fence[2] === undefined || fence[2] === '' ? undefined : fence[2];\n blocks.push({ type: 'code', language, text: body.join('\\n') });\n continue;\n }\n\n if (isBlank(line)) {\n flush();\n continue;\n }\n\n // Raw HTML is left out, as on the web; a paragraph keeps any tag that appears inside it.\n if (paragraph.length === 0 && HTML_COMMENT.test(line)) {\n flush();\n while (index < lines.length && !lines[index]!.includes('-->')) index += 1;\n continue;\n }\n if (paragraph.length === 0 && (HTML_BLOCK.test(line) || HTML_TAG_LINE.test(line))) {\n flush();\n while (index + 1 < lines.length && !isBlank(lines[index + 1]!)) index += 1;\n continue;\n }\n const heading = HEADING.exec(line);\n if (heading !== null) {\n flush();\n const level = heading[1]!.length as MarkdownHeadingLevel;\n blocks.push({ type: 'heading', level, children: parseInline(heading[2] ?? '') });\n continue;\n }\n\n if (RULE.test(line)) {\n flush();\n blocks.push({ type: 'rule' });\n continue;\n }\n\n if (QUOTE.test(line)) {\n flush();\n const quoted: string[] = [];\n while (index < lines.length && QUOTE.test(lines[index]!)) {\n quoted.push(QUOTE.exec(lines[index]!)![1]!);\n index += 1;\n }\n index -= 1;\n blocks.push({ type: 'quote', children: parseMarkdown(quoted.join('\\n')) });\n continue;\n }\n\n const divider = lines[index + 1];\n if (line.includes('|') && divider !== undefined && divider.includes('|') && TABLE_DIVIDER.test(divider)) {\n const header = tableCells(line);\n if (header.length === tableCells(divider).length) {\n flush();\n index += 2;\n const rows: MarkdownInline[][][] = [];\n while (index < lines.length && !isBlank(lines[index]!) && lines[index]!.includes('|')) {\n const cells = tableCells(lines[index]!);\n rows.push(header.map((_, column) => parseInline(cells[column] ?? '')));\n index += 1;\n }\n index -= 1;\n blocks.push({ type: 'table', header: header.map((cell) => parseInline(cell)), rows });\n continue;\n }\n }\n const bullet = BULLET_ITEM.exec(line);\n const ordered = bullet === null ? ORDERED_ITEM.exec(line) : null;\n if (bullet !== null || ordered !== null) {\n flushParagraph();\n const isOrdered = ordered !== null;\n if (list === undefined || list.ordered !== isOrdered) {\n flushList();\n list = { ordered: isOrdered, start: isOrdered ? Number(ordered[1]) : 1, items: [] };\n }\n list.items.push([isOrdered ? ordered[2]! : bullet![1]!]);\n continue;\n }\n\n if (list !== undefined && INDENTED.test(line)) {\n list.items.at(-1)!.push(line.trim());\n continue;\n }\n\n flushList();\n paragraph.push(line);\n }\n flush();\n return blocks;\n}\n\n/** Parses one run of inline Markdown. */\nexport function parseInline(source: string): MarkdownInline[] {\n const nodes: MarkdownInline[] = [];\n let text = '';\n const pushText = (value: string) => {\n text += value;\n };\n const pushNode = (node: MarkdownInline) => {\n if (text !== '') nodes.push({ type: 'text', text });\n text = '';\n nodes.push(node);\n };\n\n let index = 0;\n while (index < source.length) {\n const char = source[index]!;\n\n if (char === '\\\\' && index + 1 < source.length && ESCAPABLE.test(source[index + 1]!)) {\n pushText(source[index + 1]!);\n index += 2;\n continue;\n }\n\n if (char === '`') {\n const span = readCodeSpan(source, index);\n if (span !== undefined) {\n pushNode({ type: 'code', text: span.text });\n index = span.end;\n continue;\n }\n const run = /^`+/.exec(source.slice(index))![0];\n pushText(run);\n index += run.length;\n continue;\n }\n\n if (char === '!' && source[index + 1] === '[') {\n const image = readLink(source, index + 1);\n if (image !== undefined) {\n pushNode({ type: 'image', src: image.href, alt: image.label });\n index = image.end;\n continue;\n }\n }\n\n if (char === '[') {\n const link = readLink(source, index);\n if (link !== undefined) {\n pushNode({ type: 'link', href: link.href, children: parseInline(link.label) });\n index = link.end;\n continue;\n }\n }\n\n if ((char === '*' || char === '_') && source[index + 1] === char) {\n const strong = readDelimited(source, index, char.repeat(2));\n if (strong !== undefined) {\n pushNode({ type: 'strong', children: parseInline(strong.inner) });\n index = strong.end;\n continue;\n }\n pushText(char.repeat(2));\n index += 2;\n continue;\n }\n\n if (char === '*' || char === '_') {\n const em = readDelimited(source, index, char);\n if (em !== undefined) {\n pushNode({ type: 'em', children: parseInline(em.inner) });\n index = em.end;\n continue;\n }\n }\n\n pushText(char);\n index += 1;\n }\n if (text !== '') nodes.push({ type: 'text', text });\n return nodes;\n}\n\n/** A code span opened by the backtick run at `start`, closed by a run of the same length. */\nfunction readCodeSpan(source: string, start: number): { text: string; end: number } | undefined {\n const run = /^`+/.exec(source.slice(start))![0];\n let search = start + run.length;\n while (search < source.length) {\n const found = source.indexOf(run, search);\n if (found === -1) return undefined;\n const after = found + run.length;\n // A longer run is not the closer; skip past all of it.\n if (source[after] === '`' || source[found - 1] === '`') {\n search = after;\n while (source[search] === '`') search += 1;\n continue;\n }\n let text = source.slice(start + run.length, found).replace(/\\n/g, ' ');\n if (text.length > 2 && text.startsWith(' ') && text.endsWith(' ') && text.trim() !== '') text = text.slice(1, -1);\n return { text, end: after };\n }\n return undefined;\n}\n\n/** `[label](href)` starting at the bracket; brackets in the label may nest. */\nfunction readLink(source: string, start: number): { label: string; href: string; end: number } | undefined {\n let depth = 0;\n let close = -1;\n for (let index = start; index < source.length; index += 1) {\n const char = source[index];\n if (char === '\\\\') {\n index += 1;\n continue;\n }\n if (char === '[') depth += 1;\n if (char === ']') {\n depth -= 1;\n if (depth === 0) {\n close = index;\n break;\n }\n }\n }\n if (close === -1 || source[close + 1] !== '(') return undefined;\n const target = /^\\(\\s*<?([^\\s()<>]*)>?(?:\\s+\"[^\"]*\")?\\s*\\)/.exec(source.slice(close + 1));\n if (target === null) return undefined;\n return { label: source.slice(start + 1, close), href: target[1]!, end: close + 1 + target[0].length };\n}\n\n/**\n * Emphasis opened by `delimiter` at `start`: the inner text may not begin or\n * end with a space, and an underscore only counts at a word boundary, so\n * `snake_case_name` stays text.\n */\nfunction readDelimited(source: string, start: number, delimiter: string): { inner: string; end: number } | undefined {\n const underscore = delimiter[0] === '_';\n if (underscore && start > 0 && WORD_CHAR.test(source[start - 1]!)) return undefined;\n const open = start + delimiter.length;\n if (open >= source.length || /\\s/.test(source[open]!)) return undefined;\n let search = open;\n while (search < source.length) {\n const found = source.indexOf(delimiter, search);\n if (found === -1) return undefined;\n const end = found + delimiter.length;\n const single = delimiter.length === 1;\n // A single delimiter skips over a double one (`*a **b** c*`), which belongs to nested bold.\n if (single && source[end] === delimiter) {\n search = end + 1;\n continue;\n }\n const inner = source.slice(open, found);\n const boundaryOk = !underscore || end >= source.length || !WORD_CHAR.test(source[end]!);\n if (inner !== '' && !/\\s/.test(inner[inner.length - 1]!) && boundaryOk) return { inner, end };\n search = found + 1;\n }\n return undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"markdown.mjs","names":[],"sources":["../../src/lib/markdown.ts"],"sourcesContent":["/*\n * A small Markdown parser for native message text: the subset an assistant\n * reply uses, parsed to plain data so the renderer and the tests share one\n * shape. Kept free of React Native so a unit test can run it in Node.\n *\n * Blocks: ATX headings, paragraphs, bullet and numbered lists, fenced code\n * (an unclosed fence runs to the end, so a streaming reply shows its code\n * before the closing fence arrives), thematic breaks, blockquotes and GFM\n * tables. HTML blocks are dropped, as the web renderer drops raw HTML.\n * Inline: code spans, bold, italic, links, images and backslash escapes.\n * A line break inside a paragraph is a space unless the line ends in two\n * spaces or a backslash, as CommonMark reads it.\n *\n * ponytail: no nested lists, setext headings or lazy quote continuation yet;\n * their source falls through as paragraph text, so nothing is lost.\n */\n\nexport type MarkdownInline =\n | { type: 'text'; text: string }\n | { type: 'code'; text: string }\n | { type: 'strong'; children: MarkdownInline[] }\n | { type: 'em'; children: MarkdownInline[] }\n | { type: 'link'; href: string; children: MarkdownInline[] }\n | { type: 'image'; src: string; alt: string };\n\nexport type MarkdownHeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\n\nexport type MarkdownBlock =\n | { type: 'heading'; level: MarkdownHeadingLevel; children: MarkdownInline[] }\n | { type: 'paragraph'; children: MarkdownInline[] }\n | { type: 'list'; ordered: boolean; start: number; items: MarkdownInline[][] }\n | { type: 'code'; language: string | undefined; text: string }\n | { type: 'rule' }\n | { type: 'quote'; children: MarkdownBlock[] }\n | { type: 'table'; header: MarkdownInline[][]; rows: MarkdownInline[][][] };\n\nconst FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})[ \\t]*([^\\s`]*)/;\nconst HEADING = /^ {0,3}(#{1,6})(?:[ \\t]+(.*?))?(?:[ \\t]+#+)?[ \\t]*$/;\nconst RULE = /^ {0,3}([-*_])(?:[ \\t]*\\1){2,}[ \\t]*$/;\nconst BULLET_ITEM = /^ {0,3}[-*+][ \\t]+(.*)$/;\nconst ORDERED_ITEM = /^ {0,3}(\\d{1,9})[.)][ \\t]+(.*)$/;\nconst INDENTED = /^[ \\t]+\\S/;\nconst QUOTE = /^ {0,3}> ?(.*)$/;\nconst TABLE_DIVIDER = /^ {0,3}\\|?[ \\t]*:?-+:?[ \\t]*(?:\\|[ \\t]*:?-+:?[ \\t]*)*\\|?[ \\t]*$/;\nconst HTML_COMMENT = /^ {0,3}<!--/;\n/** CommonMark's block-level tag names, plus the picture and image markup READMEs open with. */\nconst HTML_BLOCK =\n /^ {0,3}<\\/?(?:address|article|aside|blockquote|center|details|div|dl|figcaption|figure|footer|form|h[1-6]|header|hr|li|main|nav|ol|p|picture|section|summary|table|tbody|td|tfoot|th|thead|tr|ul)(?=[\\s/>]|$)/i;\n/** Any other tag standing alone on its line, such as `<img ...>` or `<br>`. */\nconst HTML_TAG_LINE = /^ {0,3}<\\/?[A-Za-z][A-Za-z0-9-]*(?:\\s[^<>]*)?\\/?>[ \\t]*$/;\nconst HARD_BREAK = /(?: {2,}|\\\\)$/;\nconst ESCAPABLE = /[!-/:-@[-`{-~]/;\nconst WORD_CHAR = /[\\p{L}\\p{N}]/u;\n\n// ponytail: expensive documents stay literal. A linear parser can lift these ceilings later.\nconst MAX_PARSE_CHARACTERS = 100_000;\nconst MAX_PARSE_WORK = 250_000;\nconst MAX_PARSE_DEPTH = 32;\nconst PARSE_LIMIT = new Error('Markdown parsing limit reached.');\ntype ParseBudget = { remaining: number };\n\nfunction spend(budget: ParseBudget, work: number): void {\n budget.remaining -= work;\n if (budget.remaining < 0) throw PARSE_LIMIT;\n}\n\nfunction isBlank(line: string): boolean {\n return line.trim() === '';\n}\n\n/** Joins a paragraph's lines: a space between them, or a line break after two spaces or a backslash. */\nfunction joinLines(lines: readonly string[]): string {\n return lines\n .map((line, index) => {\n const current = index === 0 ? line : line.trimStart();\n if (index === lines.length - 1) return current;\n return `${current.replace(/(?: {2,}|\\\\|[ \\t]+)$/u, '')}${HARD_BREAK.test(current) ? '\\n' : ' '}`;\n })\n .join('');\n}\n\n/** A table row's cells: outer pipes dropped, split on unescaped pipes, `\\|` read as a pipe. */\nfunction tableCells(line: string): string[] {\n let row = line.trim();\n if (row.startsWith('|')) row = row.slice(1);\n if (row.endsWith('|') && !row.endsWith('\\\\|')) row = row.slice(0, -1);\n const cells: string[] = [];\n let cell = '';\n for (let index = 0; index < row.length; index += 1) {\n const char = row[index]!;\n if (char === '\\\\' && row[index + 1] === '|') {\n cell += '|';\n index += 1;\n } else if (char === '|') {\n cells.push(cell.trim());\n cell = '';\n } else cell += char;\n }\n cells.push(cell.trim());\n return cells;\n}\n\n/** Parses a Markdown document into blocks. */\nexport function parseMarkdown(source: string): MarkdownBlock[] {\n if (source.length <= MAX_PARSE_CHARACTERS) {\n try {\n return parseBlocks(source, { remaining: MAX_PARSE_WORK }, 0);\n } catch (error) {\n if (error !== PARSE_LIMIT) throw error;\n }\n }\n return [{ type: 'paragraph', children: [{ type: 'text', text: source }] }];\n}\n\nfunction parseBlocks(source: string, budget: ParseBudget, depth: number): MarkdownBlock[] {\n if (depth > MAX_PARSE_DEPTH) throw PARSE_LIMIT;\n spend(budget, source.length + 1);\n const inline = (text: string) => parseInlineNodes(text, budget, depth + 1);\n const lines = source.replace(/\\r\\n?/g, '\\n').split('\\n');\n const blocks: MarkdownBlock[] = [];\n let paragraph: string[] = [];\n let list: { ordered: boolean; start: number; items: string[][] } | undefined;\n\n const flushParagraph = () => {\n if (paragraph.length > 0) blocks.push({ type: 'paragraph', children: inline(joinLines(paragraph)) });\n paragraph = [];\n };\n const flushList = () => {\n if (list !== undefined)\n blocks.push({\n type: 'list',\n ordered: list.ordered,\n start: list.start,\n items: list.items.map((item) => inline(joinLines(item))),\n });\n list = undefined;\n };\n const flush = () => {\n flushParagraph();\n flushList();\n };\n\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index]!;\n\n const fence = FENCE_OPEN.exec(line);\n if (fence !== null) {\n flush();\n const marker = fence[1]!;\n const close = new RegExp(`^ {0,3}${marker[0] === '`' ? '`' : '~'}{${marker.length},}[ \\\\t]*$`);\n const body: string[] = [];\n index += 1;\n while (index < lines.length && !close.test(lines[index]!)) {\n body.push(lines[index]!);\n index += 1;\n }\n const language = fence[2] === undefined || fence[2] === '' ? undefined : fence[2];\n blocks.push({ type: 'code', language, text: body.join('\\n') });\n continue;\n }\n\n if (isBlank(line)) {\n flush();\n continue;\n }\n\n // Raw HTML is left out, as on the web; a paragraph keeps any tag that appears inside it.\n if (paragraph.length === 0 && HTML_COMMENT.test(line)) {\n flush();\n while (index < lines.length && !lines[index]!.includes('-->')) index += 1;\n continue;\n }\n if (paragraph.length === 0 && (HTML_BLOCK.test(line) || HTML_TAG_LINE.test(line))) {\n flush();\n while (index + 1 < lines.length && !isBlank(lines[index + 1]!)) index += 1;\n continue;\n }\n const heading = HEADING.exec(line);\n if (heading !== null) {\n flush();\n const level = heading[1]!.length as MarkdownHeadingLevel;\n blocks.push({ type: 'heading', level, children: inline(heading[2] ?? '') });\n continue;\n }\n\n if (RULE.test(line)) {\n flush();\n blocks.push({ type: 'rule' });\n continue;\n }\n\n if (QUOTE.test(line)) {\n flush();\n const quoted: string[] = [];\n while (index < lines.length && QUOTE.test(lines[index]!)) {\n quoted.push(QUOTE.exec(lines[index]!)![1]!);\n index += 1;\n }\n index -= 1;\n blocks.push({ type: 'quote', children: parseBlocks(quoted.join('\\n'), budget, depth + 1) });\n continue;\n }\n\n const divider = lines[index + 1];\n if (line.includes('|') && divider !== undefined && divider.includes('|') && TABLE_DIVIDER.test(divider)) {\n const header = tableCells(line);\n if (header.length === tableCells(divider).length) {\n flush();\n index += 2;\n const rows: MarkdownInline[][][] = [];\n while (index < lines.length && !isBlank(lines[index]!) && lines[index]!.includes('|')) {\n const cells = tableCells(lines[index]!);\n rows.push(header.map((_, column) => inline(cells[column] ?? '')));\n index += 1;\n }\n index -= 1;\n blocks.push({ type: 'table', header: header.map((cell) => inline(cell)), rows });\n continue;\n }\n }\n const bullet = BULLET_ITEM.exec(line);\n const ordered = bullet === null ? ORDERED_ITEM.exec(line) : null;\n if (bullet !== null || ordered !== null) {\n flushParagraph();\n const isOrdered = ordered !== null;\n if (list === undefined || list.ordered !== isOrdered) {\n flushList();\n list = { ordered: isOrdered, start: isOrdered ? Number(ordered[1]) : 1, items: [] };\n }\n list.items.push([isOrdered ? ordered[2]! : bullet![1]!]);\n continue;\n }\n\n if (list !== undefined && INDENTED.test(line)) {\n list.items.at(-1)!.push(line.trim());\n continue;\n }\n\n flushList();\n paragraph.push(line);\n }\n flush();\n return blocks;\n}\n\n/** Parses one run of inline Markdown. */\nexport function parseInline(source: string): MarkdownInline[] {\n if (source.length <= MAX_PARSE_CHARACTERS) {\n try {\n return parseInlineNodes(source, { remaining: MAX_PARSE_WORK }, 0);\n } catch (error) {\n if (error !== PARSE_LIMIT) throw error;\n }\n }\n return [{ type: 'text', text: source }];\n}\n\nfunction parseInlineNodes(source: string, budget: ParseBudget, depth: number): MarkdownInline[] {\n if (depth > MAX_PARSE_DEPTH) throw PARSE_LIMIT;\n spend(budget, source.length + 1);\n const nodes: MarkdownInline[] = [];\n let text = '';\n const pushText = (value: string) => {\n text += value;\n };\n const pushNode = (node: MarkdownInline) => {\n if (text !== '') nodes.push({ type: 'text', text });\n text = '';\n nodes.push(node);\n };\n\n let index = 0;\n while (index < source.length) {\n spend(budget, 1);\n const char = source[index]!;\n\n if (char === '\\\\' && index + 1 < source.length && ESCAPABLE.test(source[index + 1]!)) {\n pushText(source[index + 1]!);\n index += 2;\n continue;\n }\n\n if (char === '`') {\n const span = readCodeSpan(source, index, budget);\n if (span !== undefined) {\n pushNode({ type: 'code', text: span.text });\n index = span.end;\n continue;\n }\n const run = /^`+/.exec(source.slice(index))![0];\n pushText(run);\n index += run.length;\n continue;\n }\n\n if (char === '!' && source[index + 1] === '[') {\n const image = readLink(source, index + 1, budget);\n if (image !== undefined) {\n pushNode({ type: 'image', src: image.href, alt: image.label });\n index = image.end;\n continue;\n }\n }\n\n if (char === '[') {\n const link = readLink(source, index, budget);\n if (link !== undefined) {\n pushNode({ type: 'link', href: link.href, children: parseInlineNodes(link.label, budget, depth + 1) });\n index = link.end;\n continue;\n }\n }\n\n if ((char === '*' || char === '_') && source[index + 1] === char) {\n const strong = readDelimited(source, index, char.repeat(2), budget);\n if (strong !== undefined) {\n pushNode({ type: 'strong', children: parseInlineNodes(strong.inner, budget, depth + 1) });\n index = strong.end;\n continue;\n }\n pushText(char.repeat(2));\n index += 2;\n continue;\n }\n\n if (char === '*' || char === '_') {\n const em = readDelimited(source, index, char, budget);\n if (em !== undefined) {\n pushNode({ type: 'em', children: parseInlineNodes(em.inner, budget, depth + 1) });\n index = em.end;\n continue;\n }\n }\n\n pushText(char);\n index += 1;\n }\n if (text !== '') nodes.push({ type: 'text', text });\n return nodes;\n}\n\n/** A code span opened by the backtick run at `start`, closed by a run of the same length. */\nfunction readCodeSpan(source: string, start: number, budget: ParseBudget): { text: string; end: number } | undefined {\n const run = /^`+/.exec(source.slice(start))![0];\n let search = start + run.length;\n while (search < source.length) {\n const found = source.indexOf(run, search);\n spend(budget, (found === -1 ? source.length : found + run.length) - search);\n if (found === -1) return undefined;\n const after = found + run.length;\n // A longer run is not the closer; skip past all of it.\n if (source[after] === '`' || source[found - 1] === '`') {\n search = after;\n while (source[search] === '`') search += 1;\n continue;\n }\n let text = source.slice(start + run.length, found).replace(/\\n/g, ' ');\n if (text.length > 2 && text.startsWith(' ') && text.endsWith(' ') && text.trim() !== '') text = text.slice(1, -1);\n return { text, end: after };\n }\n return undefined;\n}\n\n/** `[label](href)` starting at the bracket; brackets in the label may nest. */\nfunction readLink(\n source: string,\n start: number,\n budget: ParseBudget,\n): { label: string; href: string; end: number } | undefined {\n let depth = 0;\n let close = -1;\n for (let index = start; index < source.length; index += 1) {\n spend(budget, 1);\n const char = source[index];\n if (char === '\\\\') {\n index += 1;\n continue;\n }\n if (char === '[') depth += 1;\n if (char === ']') {\n depth -= 1;\n if (depth === 0) {\n close = index;\n break;\n }\n }\n }\n if (close === -1 || source[close + 1] !== '(') return undefined;\n let index = close + 2;\n const skipSpace = () => {\n while (index < source.length && /\\s/.test(source[index]!)) {\n spend(budget, 1);\n index += 1;\n }\n };\n skipSpace();\n if (source[index] === '<') index += 1;\n const hrefStart = index;\n while (index < source.length && !/[\\s()<>]/.test(source[index]!)) {\n spend(budget, 1);\n index += 1;\n }\n const href = source.slice(hrefStart, index);\n if (source[index] === '>') index += 1;\n const beforeSpace = index;\n skipSpace();\n if (index > beforeSpace && source[index] === '\"') {\n index += 1;\n while (index < source.length && source[index] !== '\"') {\n spend(budget, 1);\n index += 1;\n }\n if (source[index] !== '\"') return undefined;\n index += 1;\n skipSpace();\n }\n if (source[index] !== ')') return undefined;\n return { label: source.slice(start + 1, close), href, end: index + 1 };\n}\n\n/**\n * Emphasis opened by `delimiter` at `start`: the inner text may not begin or\n * end with a space, and an underscore only counts at a word boundary, so\n * `snake_case_name` stays text.\n */\nfunction readDelimited(\n source: string,\n start: number,\n delimiter: string,\n budget: ParseBudget,\n): { inner: string; end: number } | undefined {\n const underscore = delimiter[0] === '_';\n if (underscore && start > 0 && WORD_CHAR.test(source[start - 1]!)) return undefined;\n const open = start + delimiter.length;\n if (open >= source.length || /\\s/.test(source[open]!)) return undefined;\n let search = open;\n while (search < source.length) {\n const found = source.indexOf(delimiter, search);\n spend(budget, (found === -1 ? source.length : found + delimiter.length) - search);\n if (found === -1) return undefined;\n const end = found + delimiter.length;\n const single = delimiter.length === 1;\n // A single delimiter skips over a double one (`*a **b** c*`), which belongs to nested bold.\n if (single && source[end] === delimiter) {\n search = end + 1;\n continue;\n }\n const inner = source.slice(open, found);\n const boundaryOk = !underscore || end >= source.length || !WORD_CHAR.test(source[end]!);\n if (inner !== '' && !/\\s/.test(inner[inner.length - 1]!) && boundaryOk) return { inner, end };\n search = found + 1;\n }\n return undefined;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/po-native-components",
3
- "version": "0.0.0-alpha.33",
3
+ "version": "0.0.0-alpha.35",
4
4
  "description": "Shared React Native components for Po and its native plugins: the web component library's look and API on native parts, tuned to the Po palette.",
5
5
  "keywords": [
6
6
  "ai",
@@ -45,11 +45,11 @@
45
45
  "access": "public"
46
46
  },
47
47
  "dependencies": {
48
- "@agimon-ai/po-web-components": "0.0.0-alpha.33"
48
+ "@agimon-ai/po-web-components": "0.0.0-alpha.35"
49
49
  },
50
50
  "devDependencies": {
51
- "@agimon-ai/po-build": "0.0.0-alpha.33",
52
- "@agimon-ai/vibe-lint-plugin-po-web": "0.0.0-alpha.33",
51
+ "@agimon-ai/po-build": "0.0.0-alpha.35",
52
+ "@agimon-ai/vibe-lint-plugin-po-web": "0.0.0-alpha.35",
53
53
  "@babel/core": "7.29.7",
54
54
  "@expo/vector-icons": "15.1.1",
55
55
  "@fontsource-variable/ibm-plex-sans": "5.3.0",