@bardway/trpg-notebook-native 0.0.0-stage → 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/storage.ts", "../src/importPicker.ts", "../src/reader/ReaderHost.tsx", "../src/reader/ReaderNativeConfig.ts", "../src/reader/MarkdownView.tsx", "../src/reader/MarkdownDocumentView.tsx", "../src/NotebookReaderFrame.tsx", "../src/NotebookPage.tsx", "../src/AppMarkdown.tsx", "../src/AppMarkdownSections.tsx"],
4
+ "sourcesContent": ["import type { NotebookStorageAdapter } from '@bardway/trpg-notebook';\n\ninterface NativeNotebookStorage {\n capabilities(): Promise<string>;\n ensureWorkspace(payload: string): Promise<string>;\n readDocument(payload: string): Promise<string>;\n initializeMemoTemplate(payload: string): Promise<string>;\n importDocument(payload: string): Promise<string>;\n commitOperation(payload: string): Promise<string>;\n saveViewport(payload: string): Promise<string>;\n saveLayoutProfile(payload: string): Promise<string>;\n setActiveDocument(payload: string): Promise<string>;\n deleteSession(payload: string): Promise<string>;\n readImport(payload: string): Promise<string>;\n flush(): Promise<string>;\n}\n\nlet nativePromise: Promise<NativeNotebookStorage> | null = null;\n\nconst native = (): Promise<NativeNotebookStorage> => {\n if (!nativePromise) {\n // Lazy loading keeps session bootstrap independent of an unopened notebook.\n // There is deliberately no memory fallback for authoritative notebook data.\n nativePromise = import('expo')\n .then(({ requireNativeModule }) =>\n requireNativeModule<NativeNotebookStorage>('NotebookStorage'),\n )\n .catch(() => {\n nativePromise = null;\n throw new Error('\u624B\u8BB0\u672C\u5730\u5B58\u50A8\u4E0D\u53EF\u7528\uFF0C\u8BF7\u91CD\u65B0\u7F16\u8BD1 Android \u5E94\u7528');\n });\n }\n return nativePromise;\n};\n\nconst decode = (payload: string): unknown => JSON.parse(payload);\n\nexport const notebookNativeStorage: NotebookStorageAdapter = {\n capabilities: async () => decode(await (await native()).capabilities()),\n ensureWorkspace: async (accountId, sessionId) =>\n decode(await (await native()).ensureWorkspace(JSON.stringify({ accountId, sessionId }))),\n readDocument: async (context, documentId) =>\n decode(await (await native()).readDocument(JSON.stringify({ context, documentId }))),\n initializeMemoTemplate: async (context, documentId, template) =>\n decode(\n await (\n await native()\n ).initializeMemoTemplate(JSON.stringify({ context, documentId, template })),\n ),\n importDocument: async (context, document) =>\n decode(await (await native()).importDocument(JSON.stringify({ context, document }))),\n commitOperation: async (context, documentId, operation) =>\n decode(\n await (await native()).commitOperation(JSON.stringify({ context, documentId, operation })),\n ),\n saveViewport: async (context, documentId, viewport, memoTemplateId) => {\n await (\n await native()\n ).saveViewport(\n JSON.stringify({\n context,\n documentId,\n viewport,\n ...(memoTemplateId ? { memoTemplateId } : {}),\n }),\n );\n },\n saveLayoutProfile: async (context, documentId, layout, viewport, memoTemplateId) => {\n await (\n await native()\n ).saveLayoutProfile(\n JSON.stringify({\n context,\n documentId,\n layout,\n viewport,\n ...(memoTemplateId ? { memoTemplateId } : {}),\n }),\n );\n },\n setActiveDocument: async (context, documentId) => {\n await (await native()).setActiveDocument(JSON.stringify({ context, documentId }));\n },\n deleteSession: async (accountId, sessionId) => {\n await (await native()).deleteSession(JSON.stringify({ accountId, sessionId }));\n },\n flush: async () => {\n // An unused notebook must not make logout depend on loading a native module.\n if (nativePromise) await (await nativePromise).flush();\n },\n};\n\nexport const readNotebookMarkdownImport = async (uri: string): Promise<unknown> =>\n decode(await (await native()).readImport(JSON.stringify({ uri })));\n", "import * as DocumentPicker from 'expo-document-picker';\nimport { File, Paths } from 'expo-file-system';\n\nexport interface NotebookPickedFile {\n uri: string;\n name: string;\n size?: number;\n}\n\nexport const pickNotebookMarkdownFiles = async (): Promise<NotebookPickedFile[] | null> => {\n const result = await DocumentPicker.getDocumentAsync({\n type: '*/*',\n multiple: true,\n copyToCacheDirectory: true,\n });\n if (result.canceled) return null;\n return result.assets.map(({ uri, name, size }) => ({ uri, name, size }));\n};\n\nexport const cleanupNotebookImportCopy = (uri: string): void => {\n // Only DocumentPicker's application-owned cache is removable. Never touch a provider's source.\n const cache = Paths.cache.uri.endsWith('/') ? Paths.cache.uri : `${Paths.cache.uri}/`;\n if (!uri.startsWith(cache) || uri.includes('/../') || uri.includes('%2e')) return;\n const file = new File(uri);\n if (file.exists) file.delete();\n};\n", "import React, { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';\nimport {\n Linking,\n NativeModules,\n Platform,\n Text,\n UIManager,\n View,\n findNodeHandle,\n} from 'react-native';\nimport WebView, { type WebViewMessageEvent } from 'react-native-webview';\nimport { useTheme } from '@bardway/trpg-ui-native';\nimport { resolveTypographyTextStyle } from '@bardway/trpg-ui-native';\nconst describeSafeFailure = (value: unknown) => ({\n errorType: value instanceof Error ? value.name : 'UnknownFailure',\n});\n\nimport { ThemedButton } from '@bardway/trpg-ui-native';\nimport { safeExternalLink } from '@bardway/trpg-markdown/protocol';\nimport { createReaderChunks, ReaderChunkAssembler } from '@bardway/trpg-markdown/protocol';\nimport {\n READER_ORIGIN,\n READER_PROTOCOL_VERSION,\n READER_URL,\n type ReaderEnvelope,\n} from '@bardway/trpg-markdown/protocol';\nimport { READER_NATIVE_CONFIG } from './ReaderNativeConfig.js';\n\nconst SOURCE = { uri: READER_URL };\ntype ReaderFailureStage =\n | 'initialization'\n | 'document-transfer'\n | 'renderer'\n | 'external-link'\n | 'bridge-message'\n | 'resource-load';\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nexport interface ReaderHostHandle {\n send(type: string, payload?: unknown): void;\n}\ninterface ReaderHostProps {\n documentKey: string;\n payload: unknown;\n active?: boolean;\n viewport?: boolean;\n onEvent(type: string, payload: unknown, handle: ReaderHostHandle): void;\n onError?(message: string): void;\n}\n\nexport const ReaderHost = React.memo(function ReaderHost({\n documentKey,\n payload,\n active = true,\n viewport = false,\n onEvent,\n onError,\n}: ReaderHostProps) {\n const { theme, typography, logger } = useTheme();\n const reactId = useId();\n const instanceId = useMemo(() => `reader-${reactId}-${Date.now().toString(36)}`, [reactId]);\n const nonce = useRef(`${Math.random().toString(36)}${Math.random().toString(36)}`).current;\n const webview = useRef<WebView>(null);\n const container = useRef<View>(null);\n const [ready, setReady] = useState(false);\n const [height, setHeight] = useState(1);\n const [failure, setFailure] = useState<string | null>(null);\n const currentReady = useRef(ready);\n currentReady.current = ready;\n const errorText = resolveTypographyTextStyle(typography, {\n role: 'body',\n fontSize: 13,\n metric: 'body',\n });\n const eventRoutes = useRef(new Map<string, ReaderHostProps['onEvent']>());\n eventRoutes.current.set(documentKey, onEvent);\n if (eventRoutes.current.size > 24) {\n const first = eventRoutes.current.keys().next().value;\n if (first) eventRoutes.current.delete(first);\n }\n const currentKey = useRef(documentKey);\n currentKey.current = documentKey;\n const currentActive = useRef(active);\n currentActive.current = active;\n const errorCallback = useRef(onError);\n errorCallback.current = onError;\n const incoming = useRef(new ReaderChunkAssembler());\n const sendForKey = useCallback(\n (key: string, type: string, data?: unknown) => {\n webview.current?.postMessage(\n JSON.stringify({\n protocol: READER_PROTOCOL_VERSION,\n instanceId,\n nonce,\n documentKey: key,\n type,\n payload: data,\n }),\n );\n },\n [instanceId, nonce],\n );\n const report = useCallback(\n (message: string, stage: ReaderFailureStage, cause?: unknown) => {\n setFailure(message);\n errorCallback.current?.(message);\n logger.warn('[MarkdownReader] Reader failed', {\n instanceId,\n stage,\n ...describeSafeFailure(message),\n ...(cause === undefined ? {} : { cause: describeSafeFailure(cause) }),\n ready: currentReady.current,\n active: currentActive.current,\n });\n },\n [instanceId, logger],\n );\n const serialized = useMemo(() => JSON.stringify(payload), [payload]);\n useEffect(() => {\n if (ready) return;\n const timeout = setTimeout(\n () =>\n report('\u672C\u5730\u9605\u8BFB\u5668\u672A\u5C31\u7EEA\uFF0C\u8BF7\u68C0\u67E5 Android System WebView \u7248\u672C\u5E76\u91CD\u65B0\u6253\u5F00', 'initialization'),\n 15000,\n );\n return () => clearTimeout(timeout);\n }, [ready, report]);\n\n useEffect(() => {\n if (!ready) return;\n setFailure(null);\n const id = `${documentKey}-${Date.now()}-${Math.random()}`;\n try {\n for (const chunk of createReaderChunks(serialized, id))\n sendForKey(documentKey, 'documentChunk', chunk);\n } catch (cause: unknown) {\n report('\u6587\u6863\u8D85\u8FC7\u9605\u8BFB\u5668\u6D88\u606F\u5BB9\u91CF\uFF0C\u539F\u59CB\u6570\u636E\u5DF2\u4FDD\u7559', 'document-transfer', cause);\n }\n sendForKey(documentKey, 'setActive', currentActive.current);\n }, [ready, documentKey, serialized, sendForKey, report]);\n useEffect(() => {\n if (ready) sendForKey(documentKey, 'setActive', active);\n }, [ready, active, documentKey, sendForKey]);\n\n const processMessage = useCallback(\n function process(message: ReaderEnvelope) {\n if (message.type === 'readerReady') {\n setReady(true);\n return;\n }\n if (message.type === 'bridgeChunk') {\n const combined = incoming.current.accept(message.documentKey, message.payload);\n if (combined !== null) {\n const value: unknown = JSON.parse(combined);\n if (isRecord(value) && typeof value.type === 'string' && value.type !== 'bridgeChunk')\n process({ ...message, type: value.type, payload: value.payload });\n }\n return;\n }\n if (message.type === 'readerError') {\n if (message.documentKey !== currentKey.current) return;\n report(\n isRecord(message.payload) && typeof message.payload.message === 'string'\n ? message.payload.message\n : '\u9605\u8BFB\u5668\u52A0\u8F7D\u5931\u8D25',\n 'renderer',\n );\n return;\n }\n if (message.type === 'openExternalLink') {\n const url =\n isRecord(message.payload) && typeof message.payload.url === 'string'\n ? safeExternalLink(message.payload.url)\n : null;\n if (url && message.documentKey === currentKey.current)\n void Linking.openURL(url).catch((cause: unknown) =>\n report('\u94FE\u63A5\u65E0\u6CD5\u6253\u5F00', 'external-link', cause),\n );\n return;\n }\n if (\n message.type === 'anchorRequested' &&\n message.documentKey === currentKey.current &&\n isRecord(message.payload)\n ) {\n const y = message.payload.y;\n const bridge: unknown = NativeModules.TrpgMarkdownReaderBridge;\n const tag = findNodeHandle(container.current);\n if (\n tag !== null &&\n typeof y === 'number' &&\n Number.isFinite(y) &&\n y >= 0 &&\n y <= 1_000_000 &&\n isRecord(bridge) &&\n typeof bridge.revealAnchor === 'function'\n )\n bridge.revealAnchor(tag, y);\n return;\n }\n if (\n message.type === 'blockMetrics' &&\n message.documentKey === currentKey.current &&\n isRecord(message.payload)\n ) {\n const next = message.payload.height;\n if (typeof next === 'number' && Number.isFinite(next) && next > 0 && next <= 1_000_000)\n setHeight((old) => (Math.abs(old - next) < 1 ? old : Math.ceil(next)));\n }\n eventRoutes.current.get(message.documentKey)?.(message.type, message.payload, {\n send: (type, data) => sendForKey(message.documentKey, type, data),\n });\n },\n [report, sendForKey],\n );\n\n const onMessage = useCallback(\n (event: WebViewMessageEvent) => {\n if (\n !event.nativeEvent.url.startsWith(`${READER_ORIGIN}/reader/`) ||\n event.nativeEvent.data.length > 100_000\n )\n return;\n try {\n const value: unknown = JSON.parse(event.nativeEvent.data);\n if (\n !isRecord(value) ||\n value.protocol !== READER_PROTOCOL_VERSION ||\n value.instanceId !== instanceId ||\n value.nonce !== nonce ||\n typeof value.documentKey !== 'string' ||\n value.documentKey.length > 512 ||\n typeof value.type !== 'string'\n )\n return;\n processMessage({\n protocol: READER_PROTOCOL_VERSION,\n instanceId,\n nonce,\n documentKey: value.documentKey,\n type: value.type,\n payload: value.payload,\n });\n } catch (cause: unknown) {\n report('\u9605\u8BFB\u5668\u6D88\u606F\u65E0\u6548', 'bridge-message', cause);\n }\n },\n [instanceId, nonce, processMessage, report],\n );\n\n if (Platform.OS !== 'android' || !UIManager.getViewManagerConfig('TrpgMarkdownWebView')) {\n return (\n <Text\n style={{\n color: theme.colors.text.muted,\n fontFamily: typography.body,\n fontSize: 14,\n lineHeight: 21,\n includeFontPadding: false,\n }}\n >\n \u5F53\u524D\u5B89\u88C5\u5305\u7F3A\u5C11\u672C\u5730\u9605\u8BFB\u5668\uFF0C\u8BF7\u91CD\u65B0\u7F16\u8BD1 Android APK\u3002\n </Text>\n );\n }\n return (\n <View\n ref={container}\n collapsable={false}\n style={viewport ? { flex: 1, minHeight: 1 } : { height: Math.max(1, height) }}\n >\n <WebView\n ref={webview}\n nativeConfig={READER_NATIVE_CONFIG}\n source={SOURCE}\n onMessage={onMessage}\n style={{ backgroundColor: 'transparent' }}\n containerStyle={{ backgroundColor: 'transparent' }}\n onLoadEnd={() => {\n setReady(false);\n sendForKey(currentKey.current, 'initialize');\n }}\n onError={(event) =>\n report('\u9605\u8BFB\u5668\u8D44\u6E90\u52A0\u8F7D\u5931\u8D25\uFF0C\u53EF\u91CD\u65B0\u6253\u5F00\u6587\u6863', 'resource-load', {\n code: event.nativeEvent.code,\n })\n }\n onRenderProcessGone={() => {\n setReady(false);\n webview.current?.reload();\n }}\n onShouldStartLoadWithRequest={(request) => request.url === READER_URL}\n javaScriptEnabled\n domStorageEnabled={false}\n allowFileAccess={false}\n allowFileAccessFromFileURLs={false}\n allowUniversalAccessFromFileURLs={false}\n mixedContentMode=\"never\"\n thirdPartyCookiesEnabled={false}\n sharedCookiesEnabled={false}\n setSupportMultipleWindows={false}\n setBuiltInZoomControls={false}\n scalesPageToFit={false}\n scrollEnabled={viewport}\n textZoom={100}\n androidLayerType=\"hardware\"\n />\n {failure ? (\n <View\n accessibilityLiveRegion=\"assertive\"\n style={{\n position: 'absolute',\n bottom: 0,\n left: 0,\n right: 0,\n backgroundColor: theme.colors.surface.actionDanger,\n borderTopWidth: 1,\n borderColor: theme.colors.status.error,\n padding: theme.layout.contentPadding.compact,\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.layout.density.spacing.groupY,\n }}\n >\n <Text\n accessibilityRole=\"alert\"\n style={[errorText, { color: theme.colors.text.primary, flex: 1 }]}\n >\n {failure}\n </Text>\n <ThemedButton\n label=\"\u91CD\u8BD5\"\n accessibilityLabel=\"\u91CD\u65B0\u52A0\u8F7D\u9605\u8BFB\u5668\"\n variant=\"danger\"\n style={{\n minHeight: theme.layout.touch.minimumTarget,\n minWidth: theme.layout.touch.minimumTarget,\n }}\n onPress={() => {\n incoming.current = new ReaderChunkAssembler();\n setFailure(null);\n setReady(false);\n webview.current?.reload();\n }}\n />\n </View>\n ) : null}\n </View>\n );\n});\n", "import { Platform, requireNativeComponent } from 'react-native';\nimport type { NativeProps } from 'react-native-webview/src/RNCWebViewNativeComponent';\n\n// Keep native view registration outside the host component's Fast Refresh boundary.\nconst ReaderNativeComponent =\n Platform.OS === 'android'\n ? requireNativeComponent<NativeProps>('TrpgMarkdownWebView')\n : undefined;\n\nexport const READER_NATIVE_CONFIG = ReaderNativeComponent\n ? { component: ReaderNativeComponent }\n : undefined;\n", "import React, { useCallback, useMemo } from 'react';\nimport { useTheme } from '@bardway/trpg-ui-native';\nimport type {\n MarkdownBlock,\n MarkdownBlockMetric,\n MarkdownScrollMode,\n MarkdownSurface,\n MarkdownTheme,\n MarkdownVariant,\n} from '@bardway/trpg-markdown/protocol';\nimport { isRecord, ReaderHost } from './ReaderHost.js';\n\nexport interface MarkdownViewProps {\n documentKey: string;\n markdown?: string;\n blocks?: readonly MarkdownBlock[];\n variant?: MarkdownVariant;\n surface?: MarkdownSurface;\n scrollMode?: MarkdownScrollMode;\n columns?: number;\n onBlockMetrics?(metrics: readonly MarkdownBlockMetric[]): void;\n}\n\nexport const useMarkdownTheme = (surface: MarkdownSurface): MarkdownTheme => {\n const { theme, typography } = useTheme();\n return useMemo(\n () => ({\n body: surface === 'paper' ? theme.colors.notebook.text : theme.colors.text.markdownBody,\n heading: surface === 'paper' ? theme.colors.notebook.text : theme.colors.text.accent,\n muted: surface === 'paper' ? theme.colors.notebook.muted : theme.colors.text.muted,\n link: surface === 'paper' ? theme.colors.notebook.link : theme.colors.text.accent,\n border: surface === 'paper' ? theme.colors.notebook.border : theme.colors.border.default,\n codeBackground:\n surface === 'paper' ? theme.colors.notebook.codeBackground : theme.colors.surface.card,\n background: surface === 'paper' ? theme.colors.notebook.paper : 'transparent',\n paperShade: surface === 'paper' ? theme.colors.notebook.paperShade : undefined,\n paperFiber: surface === 'paper' ? theme.colors.notebook.paperFiber : undefined,\n sizeScale: surface === 'paper' ? 1 : typography.sizeScale,\n narrativeFamily: typography.narrative,\n titleFamily: typography.title,\n uiFamily: typography.ui,\n warning: theme.colors.functional.warning,\n success: theme.colors.functional.success,\n }),\n [surface, theme.colors, typography],\n );\n};\n\nexport const MarkdownView = React.memo(function MarkdownView({\n documentKey,\n markdown,\n blocks,\n variant = 'body',\n surface = 'app',\n scrollMode = 'intrinsic',\n columns = 1,\n onBlockMetrics,\n}: MarkdownViewProps) {\n const theme = useMarkdownTheme(surface);\n const payload = useMemo(\n () => ({\n blocks: blocks ?? [{ id: 'body', markdown: markdown ?? '', variant }],\n theme,\n scrollMode,\n columns,\n }),\n [blocks, markdown, variant, theme, scrollMode, columns],\n );\n const onEvent = useCallback(\n (type: string, data: unknown) => {\n if (type !== 'blockMetrics' || !isRecord(data) || !Array.isArray(data.metrics)) return;\n const metrics: MarkdownBlockMetric[] = [];\n for (const entry of data.metrics) {\n if (\n !isRecord(entry) ||\n typeof entry.id !== 'string' ||\n typeof entry.top !== 'number' ||\n typeof entry.height !== 'number' ||\n !Number.isFinite(entry.top) ||\n !Number.isFinite(entry.height) ||\n entry.height < 0\n )\n return;\n metrics.push({\n id: entry.id,\n top: entry.top,\n height: entry.height,\n left: typeof entry.left === 'number' ? entry.left : 0,\n width: typeof entry.width === 'number' ? entry.width : undefined,\n });\n }\n onBlockMetrics?.(metrics);\n },\n [onBlockMetrics],\n );\n return (\n <ReaderHost\n documentKey={documentKey}\n payload={payload}\n viewport={scrollMode === 'viewport'}\n onEvent={onEvent}\n />\n );\n});\nexport const MarkdownBlocksView = MarkdownView;\n", "import React, { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { useTheme } from '@bardway/trpg-ui-native';\nimport { resolveNotebookPaperTypography } from '@bardway/trpg-notebook';\nimport { NOTEBOOK_LIMITS } from '@bardway/trpg-notebook';\nimport type {\n NotebookContext,\n NotebookDocument,\n NotebookLayoutProfile,\n NotebookOperation,\n NotebookSaveAcknowledgement,\n NotebookViewport,\n} from '@bardway/trpg-notebook';\nimport { isRecord, ReaderHost, type ReaderHostHandle } from './ReaderHost.js';\nimport { useMarkdownTheme } from './MarkdownView.js';\n\nexport interface MarkdownDocumentViewProps {\n context: NotebookContext;\n document: NotebookDocument;\n isActive: boolean;\n tool: 'pen' | 'eraser';\n retryToken?: number;\n onOperation(operation: NotebookOperation): Promise<NotebookSaveAcknowledgement>;\n onViewport(viewport: NotebookViewport): Promise<void> | void;\n onLayoutProfile(profile: NotebookLayoutProfile, viewport?: NotebookViewport): Promise<void>;\n onError(message: string): void;\n onReady?(): void;\n onSaveStateChange?(state: 'saved' | 'saving' | 'failed'): void;\n}\nconst finite = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value);\nconst viewportGuard = (value: unknown): value is NotebookViewport =>\n isRecord(value) &&\n finite(value.scale) &&\n value.scale >= NOTEBOOK_LIMITS.minScale &&\n value.scale <= NOTEBOOK_LIMITS.maxScale &&\n finite(value.paperOffsetX) &&\n Math.abs(value.paperOffsetX) <= 1e7 &&\n finite(value.paperOffsetY) &&\n Math.abs(value.paperOffsetY) <= 1e7;\nconst operationGuard = (value: unknown): value is NotebookOperation => {\n if (!isRecord(value) || typeof value.opId !== 'string' || value.opId.length > 160) return false;\n if (value.type === 'eraseStrokes')\n return (\n Array.isArray(value.strokeIds) &&\n value.strokeIds.length <= 100_000 &&\n value.strokeIds.every((id) => typeof id === 'string')\n );\n if (value.type !== 'addStroke' || !isRecord(value.stroke)) return false;\n const stroke = value.stroke;\n return (\n typeof stroke.id === 'string' &&\n typeof stroke.documentId === 'string' &&\n finite(stroke.width) &&\n stroke.width > 0 &&\n finite(stroke.layoutVersion) &&\n typeof stroke.color === 'string' &&\n typeof stroke.createdAt === 'string' &&\n (stroke.coordinateSpace === undefined || stroke.coordinateSpace === 'paper') &&\n Array.isArray(stroke.points) &&\n stroke.points.length > 0 &&\n stroke.points.length <= 100_000 &&\n stroke.points.every(\n (point) =>\n isRecord(point) &&\n finite(point.x) &&\n finite(point.y) &&\n (point.pressure === undefined || finite(point.pressure)),\n )\n );\n};\nconst layoutGuard = (value: unknown): value is NotebookLayoutProfile =>\n isRecord(value) &&\n ['rendererVersion', 'fontAssetVersion', 'styleProfileId', 'fingerprint'].every(\n (key) => typeof value[key] === 'string',\n ) &&\n ['layoutVersion', 'paperWidth', 'paperHeight', 'contentWidth'].every(\n (key) => finite(value[key]) && Number(value[key]) > 0,\n ) &&\n (value.memoTemplateId === undefined || typeof value.memoTemplateId === 'string');\n\nexport const MarkdownDocumentView = React.memo(function MarkdownDocumentView(\n props: MarkdownDocumentViewProps,\n) {\n const { theme } = useTheme();\n const markdownTheme = useMarkdownTheme('paper');\n const handle = useRef<ReaderHostHandle | null>(null);\n const templateId = props.document.memoTemplate?.id;\n const key = `${props.context.accountId}/${props.context.sessionId}/${props.context.generation}/${props.document.id}${templateId ? `/template/${templateId}` : ''}`;\n const activeKey = useRef(key);\n activeKey.current = key;\n const activeDocumentId = useRef(props.document.id);\n activeDocumentId.current = props.document.id;\n const payload = useMemo(() => {\n const paperTypography = resolveNotebookPaperTypography(props.document);\n return {\n document: props.document,\n context: props.context,\n theme: { ...markdownTheme, sizeScale: paperTypography.sizeScale },\n profile: {\n rendererVersion: '1',\n fontAssetVersion: '1',\n styleProfileId: paperTypography.styleProfileId,\n paperWidth: 1600,\n inkColor: theme.colors.notebook.ink,\n inkWidth: 3,\n },\n };\n }, [props.document, props.context, markdownTheme, theme.colors.notebook.ink]);\n useEffect(() => {\n handle.current?.send('setTool', props.tool);\n }, [props.tool]);\n useEffect(() => {\n if (props.retryToken) handle.current?.send('retrySaves');\n }, [props.retryToken]);\n const onEvent = useCallback(\n (type: string, payloadValue: unknown, bridge: ReaderHostHandle) => {\n if (key === activeKey.current) handle.current = bridge;\n if (!isRecord(payloadValue)) return;\n // A delayed event from the pre-template page must never overwrite the initialized memo.\n if (\n (key !== activeKey.current && props.document.id === activeDocumentId.current) ||\n payloadValue.memoTemplateId !== templateId\n )\n return;\n if (type === 'layoutReady' && layoutGuard(payloadValue.layout)) {\n if (key !== activeKey.current || payloadValue.layout.memoTemplateId !== templateId) return;\n if (payloadValue.viewport !== undefined && !viewportGuard(payloadValue.viewport)) {\n bridge.send('layoutAccepted', false);\n props.onError('\u7EB8\u9762\u9605\u8BFB\u4F4D\u7F6E\u65E0\u6548\uFF0C\u5DF2\u4FDD\u7559\u539F\u59CB\u6570\u636E');\n return;\n }\n void props\n .onLayoutProfile(payloadValue.layout, payloadValue.viewport)\n .then(() => {\n if (key !== activeKey.current) return;\n bridge.send('layoutAccepted', true);\n bridge.send('setTool', props.tool);\n props.onReady?.();\n })\n .catch((error) => {\n if (key !== activeKey.current) return;\n bridge.send('layoutAccepted', false);\n props.onError(error instanceof Error ? error.message : '\u7EB8\u9762\u7248\u672C\u4E0D\u517C\u5BB9\uFF0C\u7B14\u8FF9\u5DF2\u4FDD\u7559');\n });\n } else if (\n (type === 'strokeCommitted' || type === 'strokesErased') &&\n operationGuard(payloadValue.operation)\n ) {\n const operation = payloadValue.operation;\n void props\n .onOperation(operation)\n .then((ack) => bridge.send('saveAcknowledged', ack))\n .catch((error) => {\n const message = error instanceof Error ? error.message : '\u4FDD\u5B58\u5931\u8D25\uFF0C\u53EF\u91CD\u8BD5';\n bridge.send('saveFailed', { opId: operation.opId, message });\n props.onError(message);\n });\n } else if (type === 'viewportChanged' && isRecord(payloadValue.viewport)) {\n const { scale, paperOffsetX, paperOffsetY } = payloadValue.viewport;\n if (\n finite(scale) &&\n finite(paperOffsetX) &&\n finite(paperOffsetY) &&\n scale >= 0.1 &&\n scale <= 8\n )\n void Promise.resolve(props.onViewport({ scale, paperOffsetX, paperOffsetY })).catch(() =>\n props.onError('\u9605\u8BFB\u4F4D\u7F6E\u4FDD\u5B58\u5931\u8D25'),\n );\n } else if (type === 'saveStateChanged') {\n const state = payloadValue.state;\n if (state === 'saved' || state === 'saving' || state === 'failed')\n props.onSaveStateChange?.(state);\n }\n },\n [props, key, templateId],\n );\n return (\n <ReaderHost\n documentKey={key}\n payload={payload}\n active={props.isActive}\n viewport\n onEvent={onEvent}\n onError={props.onError}\n />\n );\n});\n", "import React, { useRef } from 'react';\nimport { ActivityIndicator, StyleSheet, View } from 'react-native';\nimport {\n MarkdownDocumentView,\n type MarkdownDocumentViewProps,\n} from './reader/MarkdownDocumentView.js';\n\ninterface NotebookReaderFrameProps {\n scopeKey: string;\n readerProps: MarkdownDocumentViewProps | null;\n loading: boolean;\n active: boolean;\n loadingColor: string;\n loadingBackground: string;\n children?: React.ReactNode;\n}\n\n/** A session keeps its one reader even while the next document is being read. */\nexport function NotebookReaderFrame({\n scopeKey,\n readerProps,\n loading,\n active,\n loadingColor,\n loadingBackground,\n children,\n}: NotebookReaderFrameProps) {\n const retained = useRef<{ scopeKey: string; props: MarkdownDocumentViewProps } | null>(null);\n if (retained.current?.scopeKey !== scopeKey) retained.current = null;\n if (readerProps) retained.current = { scopeKey, props: readerProps };\n const ready = retained.current?.props;\n return (\n <View style={styles.root}>\n {ready ? (\n <MarkdownDocumentView\n key={scopeKey}\n {...ready}\n isActive={active && !loading && readerProps !== null}\n />\n ) : !loading ? (\n children\n ) : null}\n {loading ? (\n <View\n pointerEvents=\"auto\"\n accessibilityLabel=\"\u6B63\u5728\u8BFB\u53D6\u624B\u8BB0\u6B63\u6587\"\n style={[styles.loading, { backgroundColor: loadingBackground }]}\n >\n <ActivityIndicator color={loadingColor} />\n </View>\n ) : null}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n root: { flex: 1, minHeight: 0 },\n loading: { ...StyleSheet.absoluteFillObject, alignItems: 'center', justifyContent: 'center' },\n});\n", "import React, { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n ActivityIndicator,\n AppState,\n Pressable,\n ScrollView,\n StyleSheet,\n Text,\n View,\n} from 'react-native';\nimport { useTheme } from '@bardway/trpg-ui-native';\nimport { resolveTypographyTextStyle } from '@bardway/trpg-ui-native';\nimport { ThemedButton } from '@bardway/trpg-ui-native';\n\nimport { useNotebookWorkspace, type WorkspaceInput } from '@bardway/trpg-notebook/react';\nimport { NotebookReaderFrame } from './NotebookReaderFrame.js';\n\nexport interface NotebookPageProps extends WorkspaceInput {\n accountId: string;\n sessionId: string;\n isActive: boolean;\n}\n\nexport const NotebookPage = React.memo(function NotebookPage({\n accountId,\n sessionId,\n isActive,\n repository,\n importMarkdown,\n diagnostics,\n memoTemplate,\n}: NotebookPageProps) {\n const { theme, typography } = useTheme();\n const state = useNotebookWorkspace({\n accountId,\n sessionId,\n repository,\n importMarkdown,\n diagnostics,\n memoTemplate,\n });\n const documentTabs = [...(state.workspace?.documents ?? [])].sort(\n (left, right) => Number(right.kind === 'memo') - Number(left.kind === 'memo'),\n );\n const flushWorkspace = state.flush;\n const [tool, setTool] = useState<'pen' | 'eraser'>('pen');\n const [retryToken, setRetryToken] = useState(0);\n const [appActive, setAppActive] = useState(\n AppState.currentState !== 'background' && AppState.currentState !== 'inactive',\n );\n const tabScrollRef = useRef<ScrollView>(null);\n const tabPositions = useRef(new Map<string, number>());\n const tabWidthRef = useRef(0);\n const uiText = resolveTypographyTextStyle(typography, {\n role: 'ui',\n fontSize: 13,\n metric: 'control',\n centerVertically: true,\n });\n const bodyText = resolveTypographyTextStyle(typography, {\n role: 'body',\n fontSize: 13,\n metric: 'body',\n });\n\n const revealTab = useCallback((id: string): void => {\n const x = tabPositions.current.get(id);\n if (x !== undefined)\n tabScrollRef.current?.scrollTo({\n x: Math.max(0, x - tabWidthRef.current / 3),\n animated: false,\n });\n }, []);\n\n useEffect(() => {\n if (state.document) revealTab(state.document.id);\n }, [state.document?.id, state.document, revealTab]);\n\n useEffect(() => {\n setTool('pen');\n }, [accountId, sessionId]);\n useEffect(() => {\n const subscription = AppState.addEventListener('change', (next) => {\n setAppActive(next === 'active');\n if (next !== 'active') void flushWorkspace();\n });\n return () => subscription.remove();\n }, [flushWorkspace]);\n\n const retry = (): void => {\n if (state.failedSelectionId) void state.selectDocument(state.failedSelectionId);\n else if (!state.document) state.reload();\n else {\n state.prepareRetry();\n void flushWorkspace();\n setRetryToken((value) => value + 1);\n }\n };\n const hasCurrentWorkspace =\n state.workspace?.context.accountId === accountId &&\n state.workspace.context.sessionId === sessionId;\n const readerLoading = state.loading || (state.workspace !== null && !hasCurrentWorkspace);\n const hasFailure = Boolean(state.error) || state.saveState === 'failed';\n const failureMessage =\n state.error || (state.saveState === 'failed' ? '\u624B\u8BB0\u4FDD\u5B58\u5931\u8D25\uFF0C\u5DF2\u4FDD\u5B58\u5185\u5BB9\u4FDD\u7559\u3002\u8BF7\u91CD\u8BD5\u3002' : null);\n const statusLabel = hasFailure\n ? null\n : readerLoading\n ? '\u8BFB\u53D6\u4E2D'\n : state.saveState === 'saving'\n ? '\u4FDD\u5B58\u4E2D'\n : null;\n\n return (\n <View style={styles.root}>\n <View style={[styles.toolbar, { minHeight: theme.layout.density.chrome.toolbarHeight }]}>\n <ScrollView\n ref={tabScrollRef}\n horizontal\n showsHorizontalScrollIndicator={false}\n style={styles.tabScroller}\n contentContainerStyle={styles.tabs}\n onLayout={(event) => {\n tabWidthRef.current = event.nativeEvent.layout.width;\n if (state.document) revealTab(state.document.id);\n }}\n accessibilityRole=\"tablist\"\n >\n {documentTabs.map((item) => {\n const selected = state.document?.id === item.id;\n return (\n <Pressable\n key={item.id}\n accessibilityRole=\"tab\"\n accessibilityLabel={item.title}\n accessibilityState={{ selected }}\n onPress={() => {\n void state.selectDocument(item.id);\n }}\n onLayout={(event) => {\n tabPositions.current.set(item.id, event.nativeEvent.layout.x);\n if (selected) revealTab(item.id);\n }}\n style={[\n styles.tab,\n {\n minHeight: theme.layout.touch.minimumTarget,\n backgroundColor: selected\n ? theme.colors.surface.tabActive\n : theme.colors.surface.tileAlt,\n borderColor: selected\n ? theme.colors.border.active\n : theme.colors.border.default,\n },\n ]}\n >\n <Text\n numberOfLines={1}\n style={[\n uiText,\n { color: selected ? theme.colors.text.accent : theme.colors.text.primary },\n ]}\n >\n {item.title}\n </Text>\n </Pressable>\n );\n })}\n <Pressable\n onPress={() => {\n void state.importDocuments();\n }}\n disabled={state.importing || !state.workspace}\n accessibilityRole=\"button\"\n accessibilityLabel=\"\u5BFC\u5165 Markdown \u8865\u5145\u8D44\u6599\"\n accessibilityState={{\n disabled: state.importing || !state.workspace,\n busy: state.importing,\n }}\n style={[\n styles.add,\n {\n minHeight: theme.layout.touch.minimumTarget,\n minWidth: theme.layout.touch.minimumTarget,\n borderColor: theme.colors.border.default,\n backgroundColor: theme.colors.surface.tileAlt,\n },\n ]}\n >\n {state.importing ? (\n <ActivityIndicator color={theme.colors.text.accent} />\n ) : (\n <Text style={[uiText, { color: theme.colors.text.accent }]}>+</Text>\n )}\n </Pressable>\n </ScrollView>\n <View style={styles.toolGroup} accessibilityRole=\"radiogroup\">\n {(['pen', 'eraser'] as const).map((value, index) => (\n <Pressable\n key={value}\n accessibilityRole=\"radio\"\n accessibilityLabel={value === 'pen' ? '\u753B\u7B14' : '\u6574\u7B14\u6A61\u76AE'}\n accessibilityState={{ checked: tool === value }}\n onPress={() =>\n setTool((current) => (value === 'eraser' && current === 'eraser' ? 'pen' : value))\n }\n style={[\n styles.tool,\n index === 0 ? styles.toolFirst : styles.toolLast,\n {\n minHeight: theme.layout.touch.minimumTarget,\n borderColor:\n tool === value ? theme.colors.border.active : theme.colors.border.default,\n backgroundColor:\n tool === value ? theme.colors.surface.tabActive : theme.colors.surface.tileAlt,\n },\n ]}\n >\n <Text\n style={[\n uiText,\n { color: tool === value ? theme.colors.text.accent : theme.colors.text.primary },\n ]}\n >\n {value === 'pen' ? '\u753B\u7B14' : '\u6A61\u76AE'}\n </Text>\n </Pressable>\n ))}\n </View>\n </View>\n {failureMessage ? (\n <View\n accessibilityLiveRegion=\"assertive\"\n style={[\n styles.failure,\n {\n backgroundColor: theme.colors.surface.actionDanger,\n borderColor: theme.colors.status.error,\n padding: theme.layout.contentPadding.compact,\n gap: theme.layout.density.spacing.groupY,\n },\n ]}\n >\n <Text\n accessibilityRole=\"alert\"\n style={[bodyText, { color: theme.colors.text.primary, flex: 1 }]}\n >\n {failureMessage}\n </Text>\n <ThemedButton\n label=\"\u91CD\u8BD5\"\n accessibilityLabel=\"\u91CD\u8BD5\u8BFB\u53D6\u6216\u4FDD\u5B58\u624B\u8BB0\"\n onPress={retry}\n variant=\"danger\"\n style={{\n minHeight: theme.layout.touch.minimumTarget,\n minWidth: theme.layout.touch.minimumTarget,\n }}\n />\n </View>\n ) : null}\n {state.notice ? (\n <Text numberOfLines={3} style={[bodyText, { color: theme.colors.text.secondary }]}>\n {state.notice}\n </Text>\n ) : null}\n <View style={styles.reader}>\n <NotebookReaderFrame\n scopeKey={`${accountId}/${sessionId}`}\n active={isActive && appActive}\n loading={readerLoading}\n loadingColor={theme.colors.text.accent}\n loadingBackground={theme.colors.surface.overlay}\n readerProps={\n !readerLoading && hasCurrentWorkspace && state.document && state.workspace\n ? {\n context: state.workspace.context,\n document: state.document,\n isActive: isActive && appActive,\n tool,\n retryToken,\n onOperation: state.commitOperation,\n onViewport: state.saveViewport,\n onLayoutProfile: state.saveLayoutProfile,\n onError: state.reportError,\n onSaveStateChange: state.reportSaveState,\n }\n : null\n }\n >\n <View style={styles.empty}>\n <Text style={[bodyText, { color: theme.colors.text.secondary }]}>\n \u624B\u8BB0\u5C1A\u672A\u8BFB\u53D6\u3002\u6B63\u6587\u548C\u7B14\u8FF9\u4FDD\u7559\u5728\u672C\u673A\u3002\n </Text>\n <ThemedButton label=\"\u91CD\u8BD5\" onPress={state.reload} variant=\"secondary\" />\n </View>\n </NotebookReaderFrame>\n {statusLabel ? (\n <View\n pointerEvents=\"none\"\n style={[\n styles.status,\n {\n minHeight: theme.layout.touch.minimumTarget,\n backgroundColor: theme.colors.surface.panel,\n },\n ]}\n accessibilityRole=\"text\"\n accessibilityLabel={statusLabel}\n >\n <Text style={[uiText, { color: theme.colors.text.secondary }]}>{statusLabel}</Text>\n </View>\n ) : null}\n </View>\n </View>\n );\n});\n\nconst styles = StyleSheet.create({\n root: { flex: 1, minHeight: 0, gap: 6 },\n toolbar: { flexDirection: 'row', alignItems: 'center', gap: 12, flexShrink: 0 },\n tabScroller: { flex: 1, minWidth: 0 },\n tabs: { flexDirection: 'row', alignItems: 'stretch', gap: 4 },\n tab: {\n minWidth: 88,\n maxWidth: 260,\n borderWidth: 1,\n borderRadius: 5,\n paddingHorizontal: 12,\n justifyContent: 'center',\n },\n add: { borderWidth: 1, borderRadius: 5, justifyContent: 'center', alignItems: 'center' },\n toolGroup: { flexDirection: 'row', flexShrink: 0 },\n failure: { flexDirection: 'row', alignItems: 'center', borderWidth: 1, borderRadius: 5 },\n status: {\n position: 'absolute',\n top: 6,\n left: 6,\n zIndex: 1,\n borderRadius: 5,\n paddingHorizontal: 12,\n justifyContent: 'center',\n },\n tool: {\n borderWidth: 1,\n paddingHorizontal: 12,\n minWidth: 64,\n justifyContent: 'center',\n alignItems: 'center',\n },\n toolFirst: { borderTopLeftRadius: 5, borderBottomLeftRadius: 5 },\n toolLast: { borderTopRightRadius: 5, borderBottomRightRadius: 5, marginLeft: -1 },\n reader: { flex: 1, minHeight: 0, borderRadius: 5, overflow: 'hidden' },\n empty: { flex: 1, gap: 12, justifyContent: 'center', alignItems: 'center' },\n});\n", "import React from 'react';\nimport { MarkdownView } from './reader/MarkdownView.js';\nimport type { MarkdownVariant } from '@bardway/trpg-markdown/protocol';\n\ninterface AppMarkdownProps {\n children: string;\n variant?: MarkdownVariant;\n documentKey?: string;\n}\n\n/** Compatibility entry for a single body section inside a native scroll owner. */\nexport const AppMarkdown = React.memo(function AppMarkdown({\n children,\n variant = 'body',\n documentKey,\n}: AppMarkdownProps) {\n const instanceKey = React.useId();\n return (\n <MarkdownView\n documentKey={documentKey ?? `app-markdown:${instanceKey}`}\n markdown={children}\n variant={variant}\n surface=\"app\"\n scrollMode=\"intrinsic\"\n />\n );\n});\n", "import React, { useCallback, useMemo, useRef, useState } from 'react';\nimport { View, type StyleProp, type ViewStyle } from 'react-native';\n\nimport { MarkdownBlocksView } from './reader/MarkdownView.js';\nimport type { MarkdownBlockMetric, MarkdownVariant } from '@bardway/trpg-markdown/protocol';\n\nexport interface AppMarkdownSection {\n id: string;\n markdown: string;\n header?: React.ReactNode;\n headerHeight?: number;\n leading?: React.ReactNode;\n variant?: MarkdownVariant;\n fragments?: readonly string[];\n}\n\ninterface AppMarkdownSectionsProps {\n documentKey: string;\n sections: readonly AppMarkdownSection[];\n variant?: MarkdownVariant;\n leadingWidth?: number;\n columns?: number;\n style?: StyleProp<ViewStyle>;\n}\n\nconst EMPTY_HEADER_HEIGHTS: Readonly<Record<string, number>> = {};\n\n/** One reader per native panel, with native labels in measured reserved spaces. */\nexport const AppMarkdownSections = React.memo(function AppMarkdownSections({\n documentKey,\n sections,\n variant = 'body',\n leadingWidth = 0,\n columns = 1,\n style,\n}: AppMarkdownSectionsProps) {\n const activeDocumentKey = useRef(documentKey);\n activeDocumentKey.current = documentKey;\n const [measurement, setMeasurement] = useState<{\n documentKey: string;\n metrics: readonly MarkdownBlockMetric[];\n } | null>(null);\n const [headerMeasurement, setHeaderMeasurement] = useState<{\n documentKey: string;\n heights: Readonly<Record<string, number>>;\n }>({ documentKey, heights: {} });\n const headerHeights =\n headerMeasurement.documentKey === documentKey\n ? headerMeasurement.heights\n : EMPTY_HEADER_HEIGHTS;\n const blocks = useMemo(\n () =>\n sections.map((section) => ({\n id: section.id,\n markdown: section.markdown,\n variant: section.variant ?? variant,\n beforeSpace: Math.max(0, section.headerHeight ?? 0, headerHeights[section.id] ?? 0),\n fragments: section.fragments,\n })),\n [sections, variant, headerHeights],\n );\n const onBlockMetrics = useCallback(\n (metrics: readonly MarkdownBlockMetric[]) => {\n if (activeDocumentKey.current !== documentKey) return;\n setMeasurement((current) => {\n if (\n current?.documentKey === documentKey &&\n current.metrics.length === metrics.length &&\n current.metrics.every(\n (value, index) =>\n value.id === metrics[index].id &&\n value.top === metrics[index].top &&\n value.height === metrics[index].height &&\n value.left === metrics[index].left &&\n value.width === metrics[index].width,\n )\n ) {\n return current;\n }\n return { documentKey, metrics };\n });\n },\n [documentKey],\n );\n const metrics = measurement?.documentKey === documentKey ? measurement.metrics : [];\n const metricsById = new Map(metrics.map((metric) => [metric.id, metric]));\n\n return (\n <View style={style}>\n <View style={{ marginLeft: leadingWidth }}>\n <MarkdownBlocksView\n documentKey={documentKey}\n blocks={blocks}\n columns={columns}\n variant={variant}\n surface=\"app\"\n scrollMode=\"intrinsic\"\n onBlockMetrics={onBlockMetrics}\n />\n </View>\n <View\n pointerEvents=\"box-none\"\n style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}\n >\n {sections.map((section) => {\n const metric = metricsById.get(section.id);\n if (!metric) return null;\n return (\n <React.Fragment key={section.id}>\n {section.leading && leadingWidth > 0 ? (\n <View\n testID={`markdown-leading-${section.id}`}\n style={{\n position: 'absolute',\n top: metric.top,\n left: 0,\n width: leadingWidth,\n height: metric.height,\n justifyContent: 'center',\n }}\n >\n {section.leading}\n </View>\n ) : null}\n {section.header ? (\n <View\n testID={`markdown-header-${section.id}`}\n onLayout={(event) => {\n const height = Math.ceil(event.nativeEvent.layout.height);\n if (!Number.isFinite(height) || height < 0) return;\n setHeaderMeasurement((current) => {\n const heights = current.documentKey === documentKey ? current.heights : {};\n if (heights[section.id] === height) return current;\n return { documentKey, heights: { ...heights, [section.id]: height } };\n });\n }}\n style={{\n position: 'absolute',\n top: metric.top,\n left: leadingWidth + (metric.left ?? 0),\n ...(metric.width === undefined ? { right: 0 } : { width: metric.width }),\n minHeight: section.headerHeight,\n justifyContent: 'center',\n }}\n >\n {section.header}\n </View>\n ) : null}\n </React.Fragment>\n );\n })}\n </View>\n </View>\n );\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["Platform", "ReaderHost", "Platform", "React", "useCallback", "useMemo", "useTheme", "jsx", "useTheme", "useMemo", "React", "MarkdownView", "useCallback", "React", "useCallback", "useEffect", "useMemo", "useRef", "useTheme", "jsx", "React", "MarkdownDocumentView", "useTheme", "useRef", "useMemo", "useEffect", "useCallback", "useRef", "View", "jsx", "jsxs", "useRef", "View", "React", "useCallback", "useEffect", "useRef", "useState", "ActivityIndicator", "StyleSheet", "Text", "View", "useTheme", "resolveTypographyTextStyle", "ThemedButton", "jsx", "jsxs", "React", "NotebookPage", "useTheme", "useState", "useRef", "resolveTypographyTextStyle", "useCallback", "useEffect", "View", "styles", "Text", "ActivityIndicator", "ThemedButton", "StyleSheet", "React", "jsx", "React", "AppMarkdown", "React", "useCallback", "useMemo", "useRef", "useState", "View", "jsx", "jsxs", "React", "AppMarkdownSections", "useRef", "useState", "useMemo", "useCallback", "metrics", "View"]
7
+ }
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import type { NotebookContext, NotebookDocument, NotebookLayoutProfile, NotebookOperation, NotebookSaveAcknowledgement, NotebookViewport } from '@bardway/trpg-notebook';
3
+ export interface MarkdownDocumentViewProps {
4
+ context: NotebookContext;
5
+ document: NotebookDocument;
6
+ isActive: boolean;
7
+ tool: 'pen' | 'eraser';
8
+ retryToken?: number;
9
+ onOperation(operation: NotebookOperation): Promise<NotebookSaveAcknowledgement>;
10
+ onViewport(viewport: NotebookViewport): Promise<void> | void;
11
+ onLayoutProfile(profile: NotebookLayoutProfile, viewport?: NotebookViewport): Promise<void>;
12
+ onError(message: string): void;
13
+ onReady?(): void;
14
+ onSaveStateChange?(state: 'saved' | 'saving' | 'failed'): void;
15
+ }
16
+ export declare const MarkdownDocumentView: React.NamedExoticComponent<MarkdownDocumentViewProps>;
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import type { MarkdownBlock, MarkdownBlockMetric, MarkdownScrollMode, MarkdownSurface, MarkdownTheme, MarkdownVariant } from '@bardway/trpg-markdown/protocol';
3
+ export interface MarkdownViewProps {
4
+ documentKey: string;
5
+ markdown?: string;
6
+ blocks?: readonly MarkdownBlock[];
7
+ variant?: MarkdownVariant;
8
+ surface?: MarkdownSurface;
9
+ scrollMode?: MarkdownScrollMode;
10
+ columns?: number;
11
+ onBlockMetrics?(metrics: readonly MarkdownBlockMetric[]): void;
12
+ }
13
+ export declare const useMarkdownTheme: (surface: MarkdownSurface) => MarkdownTheme;
14
+ export declare const MarkdownView: React.NamedExoticComponent<MarkdownViewProps>;
15
+ export declare const MarkdownBlocksView: React.NamedExoticComponent<MarkdownViewProps>;
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
3
+ export interface ReaderHostHandle {
4
+ send(type: string, payload?: unknown): void;
5
+ }
6
+ interface ReaderHostProps {
7
+ documentKey: string;
8
+ payload: unknown;
9
+ active?: boolean;
10
+ viewport?: boolean;
11
+ onEvent(type: string, payload: unknown, handle: ReaderHostHandle): void;
12
+ onError?(message: string): void;
13
+ }
14
+ export declare const ReaderHost: React.NamedExoticComponent<ReaderHostProps>;
15
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { NativeProps } from 'react-native-webview/src/RNCWebViewNativeComponent';
2
+ export declare const READER_NATIVE_CONFIG: {
3
+ component: import("react-native").HostComponent<NativeProps>;
4
+ } | undefined;
@@ -0,0 +1,28 @@
1
+ export interface KeyValueDriver {
2
+ getString(key: string): string | undefined;
3
+ set(key: string, value: string): void;
4
+ delete(key: string): void;
5
+ }
6
+ export interface AsyncKeyValueDriver {
7
+ getString(key: string): Promise<string | null>;
8
+ set(key: string, value: string): Promise<void>;
9
+ delete(key: string): Promise<void>;
10
+ }
11
+ export declare function createMMKVDriver(instance: {
12
+ getString(key: string): string | undefined;
13
+ set(key: string, value: string): void;
14
+ remove(key: string): boolean;
15
+ }): KeyValueDriver;
16
+ export interface SecureStorePort {
17
+ isAvailableAsync(): Promise<boolean>;
18
+ getItemAsync(key: string, options: {
19
+ keychainService: string;
20
+ }): Promise<string | null>;
21
+ setItemAsync(key: string, value: string, options: {
22
+ keychainService: string;
23
+ }): Promise<void>;
24
+ deleteItemAsync(key: string, options: {
25
+ keychainService: string;
26
+ }): Promise<void>;
27
+ }
28
+ export declare function createSecureStorageDriver(port: SecureStorePort, service: string): AsyncKeyValueDriver;
@@ -0,0 +1,35 @@
1
+ // src/storage-drivers.ts
2
+ function createMMKVDriver(instance) {
3
+ return {
4
+ getString: (key) => instance.getString(key),
5
+ set: (key, value) => instance.set(key, value),
6
+ delete: (key) => {
7
+ instance.remove(key);
8
+ }
9
+ };
10
+ }
11
+ function createSecureStorageDriver(port, service) {
12
+ const options = { keychainService: service };
13
+ async function available() {
14
+ if (!await port.isAvailableAsync()) throw new Error("Secure storage unavailable");
15
+ }
16
+ return {
17
+ async getString(key) {
18
+ await available();
19
+ return port.getItemAsync(key, options);
20
+ },
21
+ async set(key, value) {
22
+ await available();
23
+ await port.setItemAsync(key, value, options);
24
+ },
25
+ async delete(key) {
26
+ await available();
27
+ await port.deleteItemAsync(key, options);
28
+ }
29
+ };
30
+ }
31
+ export {
32
+ createMMKVDriver,
33
+ createSecureStorageDriver
34
+ };
35
+ //# sourceMappingURL=storage-drivers.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/storage-drivers.ts"],
4
+ "sourcesContent": ["export interface KeyValueDriver {\n getString(key: string): string | undefined;\n set(key: string, value: string): void;\n delete(key: string): void;\n}\nexport interface AsyncKeyValueDriver {\n getString(key: string): Promise<string | null>;\n set(key: string, value: string): Promise<void>;\n delete(key: string): Promise<void>;\n}\nexport function createMMKVDriver(instance: {\n getString(key: string): string | undefined;\n set(key: string, value: string): void;\n remove(key: string): boolean;\n}): KeyValueDriver {\n return {\n getString: (key) => instance.getString(key),\n set: (key, value) => instance.set(key, value),\n delete: (key) => {\n instance.remove(key);\n },\n };\n}\nexport interface SecureStorePort {\n isAvailableAsync(): Promise<boolean>;\n getItemAsync(key: string, options: { keychainService: string }): Promise<string | null>;\n setItemAsync(key: string, value: string, options: { keychainService: string }): Promise<void>;\n deleteItemAsync(key: string, options: { keychainService: string }): Promise<void>;\n}\nexport function createSecureStorageDriver(\n port: SecureStorePort,\n service: string,\n): AsyncKeyValueDriver {\n const options = { keychainService: service };\n async function available() {\n if (!(await port.isAvailableAsync())) throw new Error('Secure storage unavailable');\n }\n return {\n async getString(key) {\n await available();\n return port.getItemAsync(key, options);\n },\n async set(key, value) {\n await available();\n await port.setItemAsync(key, value, options);\n },\n async delete(key) {\n await available();\n await port.deleteItemAsync(key, options);\n },\n };\n}\n"],
5
+ "mappings": ";AAUO,SAAS,iBAAiB,UAId;AACjB,SAAO;AAAA,IACL,WAAW,CAAC,QAAQ,SAAS,UAAU,GAAG;AAAA,IAC1C,KAAK,CAAC,KAAK,UAAU,SAAS,IAAI,KAAK,KAAK;AAAA,IAC5C,QAAQ,CAAC,QAAQ;AACf,eAAS,OAAO,GAAG;AAAA,IACrB;AAAA,EACF;AACF;AAOO,SAAS,0BACd,MACA,SACqB;AACrB,QAAM,UAAU,EAAE,iBAAiB,QAAQ;AAC3C,iBAAe,YAAY;AACzB,QAAI,CAAE,MAAM,KAAK,iBAAiB,EAAI,OAAM,IAAI,MAAM,4BAA4B;AAAA,EACpF;AACA,SAAO;AAAA,IACL,MAAM,UAAU,KAAK;AACnB,YAAM,UAAU;AAChB,aAAO,KAAK,aAAa,KAAK,OAAO;AAAA,IACvC;AAAA,IACA,MAAM,IAAI,KAAK,OAAO;AACpB,YAAM,UAAU;AAChB,YAAM,KAAK,aAAa,KAAK,OAAO,OAAO;AAAA,IAC7C;AAAA,IACA,MAAM,OAAO,KAAK;AAChB,YAAM,UAAU;AAChB,YAAM,KAAK,gBAAgB,KAAK,OAAO;AAAA,IACzC;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,3 @@
1
+ import type { NotebookStorageAdapter } from '@bardway/trpg-notebook';
2
+ export declare const notebookNativeStorage: NotebookStorageAdapter;
3
+ export declare const readNotebookMarkdownImport: (uri: string) => Promise<unknown>;
@@ -0,0 +1,4 @@
1
+ {
2
+ "platforms": ["android"],
3
+ "android": { "modules": ["expo.modules.notebookstorage.NotebookStorageModule"] }
4
+ }
@@ -0,0 +1,149 @@
1
+ package org.bardway.trpg.reader
2
+
3
+ import android.graphics.BitmapFactory
4
+ import android.graphics.Rect
5
+ import android.net.Uri
6
+ import android.view.View
7
+ import android.view.ViewGroup
8
+ import android.webkit.WebResourceRequest
9
+ import android.webkit.WebResourceResponse
10
+ import android.webkit.WebView
11
+ import androidx.webkit.WebViewAssetLoader
12
+ import androidx.webkit.WebViewCompat
13
+ import androidx.webkit.WebViewFeature
14
+ import com.facebook.react.ReactPackage
15
+ import com.facebook.react.bridge.NativeModule
16
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
17
+ import com.facebook.react.bridge.ReactMethod
18
+ import com.facebook.react.bridge.ReactApplicationContext
19
+ import com.facebook.react.uimanager.UIManagerHelper
20
+ import com.facebook.react.uimanager.ThemedReactContext
21
+ import com.facebook.react.uimanager.ViewManager
22
+ import com.reactnativecommunity.webview.RNCWebViewClient
23
+ import com.reactnativecommunity.webview.RNCWebViewManager
24
+ import com.reactnativecommunity.webview.RNCWebViewWrapper
25
+ import java.io.ByteArrayInputStream
26
+ import java.net.HttpURLConnection
27
+ import java.net.URL
28
+
29
+ class TrpgMarkdownWebViewPackage : ReactPackage {
30
+ override fun createNativeModules(context: ReactApplicationContext): List<NativeModule> = listOf(TrpgMarkdownReaderBridge(context))
31
+ override fun createViewManagers(context: ReactApplicationContext): List<ViewManager<*, *>> = listOf(TrpgMarkdownWebViewManager())
32
+ }
33
+
34
+ /** Reveal an intrinsic reader anchor through its existing native scroll parent. */
35
+ private class TrpgMarkdownReaderBridge(private val context: ReactApplicationContext) : ReactContextBaseJavaModule(context) {
36
+ override fun getName() = "TrpgMarkdownReaderBridge"
37
+ @ReactMethod fun revealAnchor(containerTag: Int, logicalY: Double) {
38
+ if (!logicalY.isFinite() || logicalY < 0 || logicalY > 1_000_000) return
39
+ context.runOnUiQueueThread {
40
+ try {
41
+ val container = UIManagerHelper.getUIManagerForReactTag(context, containerTag)?.resolveView(containerTag) ?: return@runOnUiQueueThread
42
+ fun findWebView(view: View): WebView? {
43
+ if (view is WebView) return view
44
+ if (view is ViewGroup) for (index in 0 until view.childCount) findWebView(view.getChildAt(index))?.let { return it }
45
+ return null
46
+ }
47
+ val webView = findWebView(container) ?: return@runOnUiQueueThread
48
+ val y = (logicalY * context.resources.displayMetrics.density).toInt()
49
+ webView.requestRectangleOnScreen(Rect(0, y, webView.width, y + (32 * context.resources.displayMetrics.density).toInt()), false)
50
+ } catch (_: RuntimeException) { /* The host may have unmounted before this command. */ }
51
+ }
52
+ }
53
+ }
54
+
55
+ class TrpgMarkdownWebViewManager : RNCWebViewManager() {
56
+ override fun getName() = "TrpgMarkdownWebView"
57
+ override fun addEventEmitters(context: ThemedReactContext, view: RNCWebViewWrapper) {
58
+ view.webView.webViewClient = ReaderClient(context)
59
+ view.webView.setDownloadListener(null)
60
+ }
61
+ override fun setMessagingEnabled(view: RNCWebViewWrapper, value: Boolean) {
62
+ super.setMessagingEnabled(view, value)
63
+ val webView = view.webView
64
+ if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
65
+ webView.removeJavascriptInterface("ReactNativeWebView")
66
+ return
67
+ }
68
+ WebViewCompat.removeWebMessageListener(webView, "ReactNativeWebView")
69
+ if (value) WebViewCompat.addWebMessageListener(webView, "ReactNativeWebView", setOf("https://appassets.androidplatform.net")) { source, message, origin, isMainFrame, _ ->
70
+ if (isMainFrame && origin.toString() == "https://appassets.androidplatform.net" && source.url == "https://appassets.androidplatform.net/reader/index.html" && (message.data?.length ?: Int.MAX_VALUE) <= 100_000)
71
+ webView.onMessage(message.data, "https://appassets.androidplatform.net/reader/index.html")
72
+ }
73
+ }
74
+ }
75
+
76
+ private class ReaderClient(private val context: ThemedReactContext) : RNCWebViewClient() {
77
+ private val origin = "appassets.androidplatform.net"
78
+ private fun response(mime: String, bytes: ByteArray): WebResourceResponse = WebResourceResponse(mime, "UTF-8", ByteArrayInputStream(bytes)).apply {
79
+ responseHeaders = mapOf("Cache-Control" to "no-store", "X-Content-Type-Options" to "nosniff")
80
+ }
81
+ private fun blocked() = response("text/plain", ByteArray(0))
82
+ private val loader = WebViewAssetLoader.Builder()
83
+ .addPathHandler("/reader/") { name ->
84
+ if (name !in setOf("index.html", "reader.js", "reader.css")) blocked()
85
+ else try { response(if (name.endsWith(".js")) "text/javascript" else if (name.endsWith(".css")) "text/css" else "text/html", context.assets.open("markdown-reader/$name").use { it.readBytes() }) } catch (_: Exception) { blocked() }
86
+ }
87
+ .addPathHandler("/fonts/") { name ->
88
+ val font = TrpgReaderFontResources.fonts[name]
89
+ if (font == null) blocked() else {
90
+ val id = context.resources.getIdentifier(font, "font", context.packageName)
91
+ if (id == 0) blocked() else WebResourceResponse("font/ttf", null, context.resources.openRawResource(id))
92
+ }
93
+ }.build()
94
+
95
+ override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean =
96
+ request.url.scheme != "https" || request.url.host != origin || request.url.path != "/reader/index.html"
97
+ override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean =
98
+ Uri.parse(url).let { it.scheme != "https" || it.host != origin || it.path != "/reader/index.html" }
99
+
100
+ override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse = intercept(request.url)
101
+ override fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse = intercept(Uri.parse(url))
102
+
103
+ private fun intercept(uri: Uri): WebResourceResponse {
104
+ if (uri.scheme != "https" || uri.host != origin) return blocked()
105
+ if (uri.path == "/image") return loadImage(uri.getQueryParameter("url"))
106
+ return loader.shouldInterceptRequest(uri) ?: blocked()
107
+ }
108
+
109
+ private fun loadImage(value: String?): WebResourceResponse {
110
+ if (value == null || value.length > 8192) return blocked()
111
+ var next = value
112
+ try {
113
+ repeat(4) {
114
+ val parsed = Uri.parse(next)
115
+ if (parsed.scheme != "https" || parsed.host == null || parsed.host == origin || parsed.userInfo != null) return blocked()
116
+ val connection = URL(next).openConnection() as HttpURLConnection
117
+ connection.instanceFollowRedirects = false
118
+ connection.connectTimeout = 8000
119
+ connection.readTimeout = 8000
120
+ connection.setRequestProperty("Accept", "image/png,image/jpeg,image/webp")
121
+ connection.setRequestProperty("Cookie", "")
122
+ try {
123
+ if (connection.responseCode in 300..399) {
124
+ next = URL(URL(next), connection.getHeaderField("Location") ?: return blocked()).toString()
125
+ } else {
126
+ if (connection.responseCode != 200 || connection.contentLengthLong > 4 * 1024 * 1024) return blocked()
127
+ val output = java.io.ByteArrayOutputStream()
128
+ connection.inputStream.use { input ->
129
+ val buffer = ByteArray(16384)
130
+ var length = input.read(buffer)
131
+ while (length >= 0) {
132
+ if (output.size() + length > 4 * 1024 * 1024) return blocked()
133
+ output.write(buffer, 0, length)
134
+ length = input.read(buffer)
135
+ }
136
+ }
137
+ val bytes = output.toByteArray()
138
+ val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
139
+ BitmapFactory.decodeByteArray(bytes, 0, bytes.size, options)
140
+ val mime = options.outMimeType
141
+ if (mime !in setOf("image/png", "image/jpeg", "image/webp") || options.outWidth <= 0 || options.outHeight <= 0 || options.outWidth.toLong() * options.outHeight > 8_000_000) return blocked()
142
+ return response(mime, bytes)
143
+ }
144
+ } finally { connection.disconnect() }
145
+ }
146
+ } catch (_: Exception) { return blocked() }
147
+ return blocked()
148
+ }
149
+ }
package/package.json CHANGED
@@ -1,6 +1,78 @@
1
1
  {
2
2
  "name": "@bardway/trpg-notebook-native",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Rule-independent notebook-native extracted from MingriAgency",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/bardway/mingriAgency_TRPGEngine.git",
10
+ "directory": "packages/notebook-native"
11
+ },
12
+ "publishConfig": {
13
+ "registry": "https://registry.npmjs.org/",
14
+ "access": "public"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "native",
19
+ "expo-module.config.json",
20
+ "app.plugin.cjs",
21
+ "LICENSE",
22
+ "NOTICE",
23
+ "android/build.gradle",
24
+ "android/src/main"
25
+ ],
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js",
30
+ "react-native": "./dist/index.js",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "./storage-drivers": {
34
+ "types": "./dist/storage-drivers.d.ts",
35
+ "import": "./dist/storage-drivers.js",
36
+ "react-native": "./dist/storage-drivers.js",
37
+ "default": "./dist/storage-drivers.js"
38
+ },
39
+ "./chunked-secure": {
40
+ "types": "./dist/chunked-secure.d.ts",
41
+ "import": "./dist/chunked-secure.js",
42
+ "react-native": "./dist/chunked-secure.js",
43
+ "default": "./dist/chunked-secure.js"
44
+ },
45
+ "./app.plugin": "./app.plugin.cjs"
46
+ },
47
+ "main": "./dist/index.js",
48
+ "types": "./dist/index.d.ts",
49
+ "dependencies": {
50
+ "@bardway/trpg-notebook": "0.1.0",
51
+ "@bardway/trpg-markdown": "0.1.0",
52
+ "@bardway/trpg-ui-native": "0.1.0"
53
+ },
54
+ "peerDependencies": {
55
+ "expo": "~55.0.0",
56
+ "expo-document-picker": "~55.0.17",
57
+ "expo-file-system": "~55.0.26",
58
+ "react": "19.2.0",
59
+ "react-native": "0.83.10",
60
+ "react-native-webview": "13.16.0",
61
+ "@expo/config-plugins": "55.0.11"
62
+ },
63
+ "scripts": {
64
+ "build": "node ../../scripts/build.mjs --only=@bardway/trpg-notebook-native"
65
+ },
66
+ "trpg": {
67
+ "entries": {
68
+ "index": "src/index.ts",
69
+ "storage-drivers": "src/storage-drivers.ts",
70
+ "chunked-secure": "src/chunked-secure.ts"
71
+ }
72
+ },
73
+ "peerDependenciesMeta": {
74
+ "@expo/config-plugins": {
75
+ "optional": true
76
+ }
77
+ }
78
+ }