aac-board-viewer 0.2.9 → 0.3.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/vue/index.ts","../../src/vue/BoardViewer.ts"],"sourcesContent":["export { BoardViewer } from './BoardViewer';\nexport type { BoardViewerProps, ButtonMetric } from '../types';\nexport type { AACTree, AACPage, AACButton } from '@willwade/aac-processors';\n","import {\n defineComponent,\n ref,\n computed,\n watch,\n onMounted,\n onBeforeUnmount,\n nextTick,\n h,\n type PropType,\n type VNode,\n type VNodeRef,\n} from 'vue';\nimport type { AACPage, AACButton } from '@willwade/aac-processors';\nimport type { BoardViewerProps, ButtonMetric } from '../types';\n\ninterface PredictionsTooltipProps {\n predictions: string[];\n label: string;\n position: { x: number; y: number };\n buttonMetricsLookup: { [buttonId: string]: ButtonMetric };\n onClose: () => void;\n onWordClick?: (word: string, effort: number) => void;\n}\n\nconst PredictionsTooltip = defineComponent({\n name: 'PredictionsTooltip',\n props: {\n predictions: { type: Array as PropType<string[]>, required: true },\n label: { type: String, required: true },\n position: {\n type: Object as PropType<{ x: number; y: number }>,\n required: true,\n },\n buttonMetricsLookup: {\n type: Object as PropType<{ [buttonId: string]: ButtonMetric }>,\n required: true,\n },\n onClose: { type: Function as PropType<() => void>, required: true },\n onWordClick: {\n type: Function as PropType<(word: string, effort: number) => void>,\n required: false,\n },\n },\n setup(props: Readonly<PredictionsTooltipProps>) {\n const handleClickOutside = (e: MouseEvent) => {\n if (e.target instanceof HTMLElement && !e.target.closest('.predictions-tooltip')) {\n props.onClose();\n }\n };\n\n onMounted(() => {\n document.addEventListener('mousedown', handleClickOutside);\n });\n\n onBeforeUnmount(() => {\n document.removeEventListener('mousedown', handleClickOutside);\n });\n\n const getPosition = () => {\n const maxX = typeof window !== 'undefined' ? window.innerWidth - 200 : props.position.x;\n const maxY = typeof window !== 'undefined' ? window.innerHeight - 150 : props.position.y;\n return {\n left: `${Math.min(props.position.x, maxX)}px`,\n top: `${Math.min(props.position.y, maxY)}px`,\n };\n };\n\n return () => {\n const positionStyle = getPosition();\n\n return h(\n 'div',\n {\n class:\n 'predictions-tooltip fixed z-50 bg-white dark:bg-gray-800 rounded-lg shadow-xl border-2 border-purple-500 p-3 max-w-xs',\n style: positionStyle,\n onClick: (e: MouseEvent) => e.stopPropagation(),\n },\n [\n h('div', { class: 'flex items-center justify-between mb-2' }, [\n h(\n 'h4',\n { class: 'text-sm font-semibold text-gray-900 dark:text-white' },\n `Word forms for \"${props.label}\"`\n ),\n h(\n 'button',\n {\n onClick: props.onClose,\n class: 'p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded',\n 'aria-label': 'Close',\n },\n [\n h(\n 'svg',\n {\n class: 'h-4 w-4 text-gray-600 dark:text-gray-400',\n fill: 'none',\n stroke: 'currentColor',\n viewBox: '0 0 24 24',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'stroke-width': 2,\n d: 'M6 18L18 6M6 6l12 12',\n }),\n ]\n ),\n ]\n ),\n ]),\n h(\n 'div',\n { class: 'flex flex-wrap gap-1' },\n props.predictions.map((word: string, idx: number) => {\n const metricForWord = Object.values(props.buttonMetricsLookup).find(\n (m: ButtonMetric) => m.label === word\n );\n const effort = metricForWord?.effort;\n\n return h(\n 'span',\n {\n key: `${word}-${idx}`,\n class:\n 'px-2 py-1 bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 rounded text-xs font-medium relative cursor-pointer hover:bg-purple-200 dark:hover:bg-purple-800 transition',\n title: `Click to add \"${word}\" to message`,\n onClick: (e: MouseEvent) => {\n e.stopPropagation();\n if (props.onWordClick) {\n props.onWordClick(word, effort || 1.0);\n props.onClose();\n }\n },\n },\n [\n effort !== undefined\n ? h(\n 'span',\n {\n class:\n 'absolute -top-1 -right-1 px-1 py-0 text-[8px] font-semibold rounded bg-blue-600 text-white shadow-xs',\n },\n effort.toFixed(1)\n )\n : null,\n word,\n ]\n );\n })\n ),\n ]\n );\n };\n },\n});\n\nexport const BoardViewer = defineComponent({\n name: 'BoardViewer',\n props: {\n tree: { type: Object as PropType<BoardViewerProps['tree']>, required: true },\n buttonMetrics: { type: Array as PropType<ButtonMetric[] | null>, default: null },\n showMessageBar: { type: Boolean, default: true },\n showEffortBadges: { type: Boolean, default: true },\n showLinkIndicators: { type: Boolean, default: true },\n initialPageId: { type: String, default: undefined },\n navigateToPageId: { type: String, default: undefined },\n highlight: { type: Object as PropType<BoardViewerProps['highlight']>, default: undefined },\n onButtonClick: {\n type: Function as PropType<(button: AACButton) => void>,\n default: undefined,\n },\n onPageChange: {\n type: Function as PropType<(pageId: string) => void>,\n default: undefined,\n },\n className: { type: String, default: '' },\n loadId: { type: String, default: undefined },\n },\n setup(props: Readonly<BoardViewerProps>) {\n const resolveInitialPageId = () => {\n if (props.initialPageId && props.tree.pages[props.initialPageId]) {\n return props.initialPageId;\n }\n if (props.tree.toolbarId && props.tree.rootId && props.tree.rootId !== props.tree.toolbarId) {\n return props.tree.rootId;\n }\n if (props.tree.rootId && props.tree.pages[props.tree.rootId]) {\n const rootPage = props.tree.pages[props.tree.rootId];\n const isToolbar =\n rootPage.name.toLowerCase().includes('toolbar') ||\n rootPage.name.toLowerCase().includes('tool bar');\n if (!isToolbar) {\n return props.tree.rootId;\n }\n }\n const startPage = Object.values(props.tree.pages).find(\n (p) => p.name.toLowerCase() === 'start'\n );\n if (startPage) {\n return startPage.id;\n }\n const nonToolbarPage = Object.values(props.tree.pages).find(\n (p) => !p.name.toLowerCase().includes('toolbar') && !p.name.toLowerCase().includes('tool bar')\n );\n if (nonToolbarPage) {\n return nonToolbarPage.id;\n }\n const pageIds = Object.keys(props.tree.pages);\n return pageIds.length > 0 ? pageIds[0] : null;\n };\n\n const currentPageId = ref<string | null>(resolveInitialPageId());\n const highlightedButtonRef = ref<HTMLButtonElement | null>(null);\n const pageHistory = ref<AACPage[]>([]);\n const message = ref('');\n const currentWordCount = ref(0);\n const currentEffort = ref(0);\n\n const predictionsTooltip = ref<PredictionsTooltipProps | null>(null);\n\n const buttonMetricsLookup = computed(() => {\n if (!props.buttonMetrics) return {};\n const lookup: { [buttonId: string]: ButtonMetric } = {};\n props.buttonMetrics.forEach((metric: ButtonMetric) => {\n lookup[metric.id] = metric;\n });\n return lookup;\n });\n\n const currentPage = computed(() =>\n currentPageId.value ? props.tree.pages[currentPageId.value] : null\n );\n\n const goToPage = (targetPageId: string | undefined | null) => {\n if (!targetPageId || !props.tree.pages[targetPageId]) return false;\n if (currentPage.value) {\n pageHistory.value = [...pageHistory.value, currentPage.value];\n }\n currentPageId.value = targetPageId;\n if (props.onPageChange) {\n props.onPageChange(targetPageId);\n }\n return true;\n };\n\n watch([() => props.tree, () => props.initialPageId], () => {\n currentPageId.value = resolveInitialPageId();\n pageHistory.value = [];\n });\n\n watch(\n () => props.navigateToPageId,\n (pageId: string | undefined) => {\n if (!pageId) return;\n if (!props.tree.pages[pageId]) return;\n currentPageId.value = pageId;\n pageHistory.value = [];\n if (props.onPageChange) {\n props.onPageChange(pageId);\n }\n }\n );\n\n watch(\n [\n currentPageId,\n () => props.highlight?.pageId,\n () => props.highlight?.x,\n () => props.highlight?.y,\n () => props.highlight?.label,\n ],\n () => {\n nextTick(() => {\n if (highlightedButtonRef.value) {\n highlightedButtonRef.value.scrollIntoView({\n behavior: 'smooth',\n block: 'center',\n inline: 'center',\n });\n }\n });\n }\n );\n\n const updateStats = (word: string, effort: number) => {\n currentWordCount.value += 1;\n currentEffort.value += effort;\n };\n\n const clearMessage = () => {\n message.value = '';\n currentWordCount.value = 0;\n currentEffort.value = 0;\n };\n\n const handleBack = () => {\n if (pageHistory.value.length > 0) {\n const previousPage = pageHistory.value[pageHistory.value.length - 1];\n pageHistory.value = pageHistory.value.slice(0, -1);\n currentPageId.value = previousPage.id;\n if (props.onPageChange) {\n props.onPageChange(previousPage.id);\n }\n }\n };\n\n const handleButtonClick = (button: AACButton) => {\n if (props.onButtonClick) {\n props.onButtonClick(button);\n }\n\n const intent = button.semanticAction?.intent\n ? String(button.semanticAction.intent)\n : undefined;\n const targetPageId = button.targetPageId || button.semanticAction?.targetId;\n const effort = buttonMetricsLookup.value[button.id]?.effort || 1;\n const textValue =\n button.semanticAction?.text || button.message || button.label || '';\n\n const deleteLastWord = () => {\n message.value = message.value\n .trim()\n .split(/\\s+/)\n .slice(0, -1)\n .join(' ');\n };\n\n const deleteLastCharacter = () => {\n message.value = message.value.slice(0, -1);\n };\n\n const appendText = (word: string) => {\n const trimmed = word || button.label || '';\n message.value = trimmed\n ? `${message.value}${message.value ? ' ' : ''}${trimmed}`\n : message.value;\n if (trimmed) {\n updateStats(trimmed, effort);\n }\n };\n\n if (intent === 'NAVIGATE_TO' && goToPage(targetPageId)) {\n return;\n }\n\n switch (intent) {\n case 'GO_BACK':\n handleBack();\n return;\n case 'GO_HOME':\n if (props.tree.rootId && goToPage(props.tree.rootId)) return;\n break;\n case 'DELETE_WORD':\n deleteLastWord();\n return;\n case 'DELETE_CHARACTER':\n deleteLastCharacter();\n return;\n case 'CLEAR_TEXT':\n clearMessage();\n return;\n case 'SPEAK_IMMEDIATE':\n case 'SPEAK_TEXT':\n case 'INSERT_TEXT':\n appendText(textValue);\n return;\n default:\n break;\n }\n\n if (targetPageId && goToPage(targetPageId)) {\n return;\n }\n\n appendText(textValue);\n };\n\n const handleShowPredictions = (button: AACButton, event: MouseEvent) => {\n event.stopPropagation();\n const predictions =\n button.predictions || (button.parameters as { predictions?: string[] })?.predictions;\n if (predictions && predictions.length > 0) {\n predictionsTooltip.value = {\n predictions,\n label: button.label,\n position: { x: event.clientX, y: event.clientY },\n buttonMetricsLookup: buttonMetricsLookup.value,\n onWordClick: (word: string, effort: number) => {\n const trimmed = word || '';\n if (trimmed) {\n message.value = `${message.value}${message.value ? ' ' : ''}${trimmed}`;\n updateStats(trimmed, effort);\n }\n },\n onClose: () => {\n predictionsTooltip.value = null;\n },\n };\n }\n };\n\n const getTextColor = (backgroundColor?: string) => {\n if (!backgroundColor) return '#111827';\n\n const hex = backgroundColor.replace('#', '');\n if (hex.length === 6) {\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\n return brightness >= 128 ? '#111827' : '#f9fafb';\n }\n\n return '#111827';\n };\n\n const renderToolbar = (toolbarPage: AACPage | null): VNode | null => {\n if (!toolbarPage) return null;\n\n return h('div', {\n class:\n 'w-16 sm:w-20 border-r border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/50',\n }, [\n h('div', { class: 'p-2' }, [\n h(\n 'p',\n {\n class:\n 'text-[10px] text-gray-500 dark:text-gray-400 text-center mb-2 font-semibold',\n },\n 'TOOLBAR'\n ),\n h(\n 'div',\n { class: 'grid gap-1' },\n toolbarPage.grid.flatMap((row: (AACButton | null)[]) =>\n row.map((button: AACButton | null) => {\n if (!button) return null;\n\n const buttonMetric = buttonMetricsLookup.value[button.id];\n const effort = buttonMetric?.effort || 0;\n\n return h(\n 'button',\n {\n key: button.id,\n onClick: () => handleButtonClick(button),\n class:\n 'aspect-square p-1 rounded border border-gray-200 dark:border-gray-700 transition flex flex-col items-center justify-center gap-0.5 hover:bg-gray-200 dark:hover:bg-gray-700 relative',\n style: {\n backgroundColor: button.style?.backgroundColor || '#f3f4f6',\n borderColor: button.style?.borderColor || '#e5e7eb',\n color:\n button.style?.fontColor || getTextColor(button.style?.backgroundColor),\n },\n title: `${button.label}\\n${button.message || ''}`,\n },\n [\n buttonMetric && props.showEffortBadges && effort > 0\n ? h(\n 'div',\n {\n class:\n 'absolute top-0 right-0 px-0.5 py-0 text-[8px] font-semibold rounded bg-blue-600 text-white',\n },\n effort.toFixed(1)\n )\n : null,\n h(\n 'span',\n {\n class:\n 'text-[8px] sm:text-[9px] text-center font-medium leading-tight line-clamp-2',\n },\n button.label\n ),\n ]\n );\n })\n )\n ),\n ]),\n ]);\n };\n\n const renderGridCells = (): (VNode | null)[] => {\n if (!currentPage.value) return [];\n const rendered = new Set<string>();\n const cells: (VNode | null)[] = [];\n\n currentPage.value.grid.forEach((row: (AACButton | null)[], rowIndex: number) => {\n row.forEach((button: AACButton | null, colIndex: number) => {\n if (!button) {\n cells.push(\n h('div', {\n key: `empty-${rowIndex}-${colIndex}`,\n class: 'aspect-square',\n })\n );\n return;\n }\n\n if (rendered.has(button.id)) {\n cells.push(null);\n return;\n }\n rendered.add(button.id);\n\n const buttonMetric = buttonMetricsLookup.value[button.id];\n const effort = buttonMetric?.effort || 0;\n const targetPageId = button.targetPageId || button.semanticAction?.targetId;\n const hasLink = targetPageId && props.tree.pages[targetPageId];\n const colSpan = button.columnSpan || 1;\n const rowSpan = button.rowSpan || 1;\n const predictions =\n button.predictions || (button.parameters as { predictions?: string[] })?.predictions;\n const hasPredictions = predictions && predictions.length > 0;\n const isPredictionCell =\n button.contentType === 'AutoContent' &&\n (button.contentSubType || '').toLowerCase() === 'prediction';\n const isWorkspace = button.contentType === 'Workspace';\n\n const isHighlighted =\n props.highlight &&\n props.highlight.pageId === currentPageId.value &&\n (props.highlight.buttonId === button.id ||\n (props.highlight.x !== undefined && props.highlight.y !== undefined\n ? button.x === props.highlight.x && button.y === props.highlight.y\n : false) ||\n (props.highlight.label && button.label === props.highlight.label));\n\n const params = button.parameters as {\n image_id?: string;\n gridPageName?: string;\n imageData?: Buffer;\n };\n\n let imageSrc: string | null = null;\n let apiUrl: string | undefined = undefined;\n\n const resolvedEntry = button.resolvedImageEntry;\n const buttonImage = button.image;\n\n if (\n resolvedEntry &&\n typeof resolvedEntry === 'string' &&\n resolvedEntry.startsWith('data:image/')\n ) {\n imageSrc = resolvedEntry;\n } else if (\n buttonImage &&\n typeof buttonImage === 'string' &&\n buttonImage.startsWith('data:image/')\n ) {\n imageSrc = buttonImage;\n } else if (\n resolvedEntry &&\n typeof resolvedEntry === 'string' &&\n !resolvedEntry.startsWith('[') &&\n !resolvedEntry.startsWith('data:image/')\n ) {\n if (props.loadId) {\n const entryPath = resolvedEntry;\n const pathMatch = entryPath.match(/^(?:Grids\\/)?(.+)$/);\n if (pathMatch) {\n apiUrl = `/api/image/${props.loadId}/${encodeURIComponent(pathMatch[1])}`;\n }\n } else {\n imageSrc = resolvedEntry;\n }\n } else if (\n buttonImage &&\n typeof buttonImage === 'string' &&\n !buttonImage.startsWith('[')\n ) {\n imageSrc = buttonImage;\n }\n\n if (\n props.loadId &&\n !imageSrc &&\n !apiUrl &&\n params &&\n params.image_id &&\n typeof params.image_id === 'string'\n ) {\n apiUrl = `/api/image/${props.loadId}/${encodeURIComponent(params.image_id)}`;\n }\n\n if (isWorkspace) {\n cells.push(\n h(\n 'div',\n {\n key: button.id,\n class:\n 'relative p-3 rounded-lg border-2 bg-gray-50 text-gray-800 flex items-center gap-2',\n style: {\n borderColor: button.style?.borderColor || '#e5e7eb',\n gridColumn: `${colIndex + 1} / span ${colSpan}`,\n gridRow: `${rowIndex + 1} / span ${rowSpan}`,\n },\n },\n [\n h('div', { class: 'font-semibold text-sm' }, button.label || 'Workspace'),\n h('div', { class: 'text-xs text-gray-500 truncate' }, message.value || 'Chat writing area'),\n ]\n )\n );\n return;\n }\n\n cells.push(\n h(\n 'button',\n {\n key: button.id,\n onClick: () => handleButtonClick(button),\n ref: isHighlighted\n ? ((el: Element | null) => {\n highlightedButtonRef.value = el as HTMLButtonElement | null;\n }) as VNodeRef\n : undefined,\n class:\n `relative aspect-square p-2 rounded-lg border-2 transition flex flex-col items-center justify-center gap-1 hover:opacity-80 hover:scale-105 active:scale-95 ${\n isHighlighted ? 'ring-4 ring-amber-400 ring-offset-2 ring-offset-white' : ''\n }`,\n style: {\n backgroundColor: button.style?.backgroundColor || '#f3f4f6',\n borderColor: button.style?.borderColor || '#e5e7eb',\n color:\n button.style?.fontColor || getTextColor(button.style?.backgroundColor),\n gridColumn: `${colIndex + 1} / span ${colSpan}`,\n gridRow: `${rowIndex + 1} / span ${rowSpan}`,\n },\n },\n [\n buttonMetric && props.showEffortBadges\n ? h(\n 'div',\n {\n class:\n 'absolute top-1 right-1 px-1.5 py-0.5 text-xs font-semibold rounded bg-blue-600 text-white shadow-sm',\n },\n effort.toFixed(1)\n )\n : null,\n hasLink && props.showLinkIndicators\n ? h('div', {\n class:\n 'absolute top-1 left-1 w-2 h-2 bg-green-500 rounded-full shadow-sm',\n })\n : null,\n hasPredictions\n ? h(\n 'div',\n {\n onClick: (event: MouseEvent) => handleShowPredictions(button, event),\n class:\n 'absolute bottom-1 right-1 px-1.5 py-0.5 text-xs font-semibold rounded bg-purple-600 text-white shadow-sm cursor-pointer hover:bg-purple-700 transition',\n title: `Has ${predictions?.length} word form${\n predictions && predictions.length > 1 ? 's' : ''\n }`,\n },\n predictions?.length\n )\n : null,\n imageSrc || apiUrl\n ? h('img', {\n src: imageSrc || apiUrl,\n alt: button.label,\n class: 'max-h-12 object-contain',\n onError: (e: Event) => {\n const target = e.target as HTMLImageElement;\n console.warn('Image failed to load:', button.label, 'src:', target.src);\n target.style.display = 'none';\n },\n })\n : null,\n h('div', { class: 'flex flex-col items-center justify-center' }, [\n h(\n 'span',\n {\n class: 'text-xs sm:text-sm text-center font-medium leading-tight line-clamp-3',\n },\n button.label\n ),\n isPredictionCell && predictions && predictions.length > 0\n ? h(\n 'div',\n {\n class:\n 'mt-1 text-[10px] sm:text-xs text-center opacity-80 space-y-0.5',\n },\n [\n ...predictions.slice(0, 3).map((prediction: string, idx: number) =>\n h('div', { key: `${button.id}-pred-${idx}` }, prediction)\n ),\n predictions.length > 3 ? h('div', '…') : null,\n ]\n )\n : null,\n ]),\n button.message && button.message !== button.label && !isPredictionCell\n ? h(\n 'span',\n { class: 'text-[10px] sm:text-xs text-center opacity-75 line-clamp-2' },\n button.message\n )\n : null,\n ]\n )\n );\n });\n });\n\n return cells;\n };\n\n return () => {\n if (!currentPage.value) {\n return h(\n 'div',\n {\n class: `flex items-center justify-center h-96 bg-gray-100 dark:bg-gray-800 rounded-lg ${\n props.className\n }`,\n },\n [\n h('div', { class: 'text-center' }, [\n h('p', { class: 'text-gray-600 dark:text-gray-400' }, 'No pages available'),\n ]),\n ]\n );\n }\n\n const toolbarPage = props.tree.toolbarId ? props.tree.pages[props.tree.toolbarId] : null;\n const gridRows = currentPage.value.grid.length;\n const gridCols = gridRows > 0 ? currentPage.value.grid[0].length : 0;\n\n return h('div', {\n class: `bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden ${props.className}`,\n }, [\n props.showMessageBar\n ? h(\n 'div',\n {\n class:\n 'p-4 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/50',\n },\n [\n h('div', { class: 'flex items-start justify-between gap-4' }, [\n h('div', { class: 'flex-1 min-w-0' }, [\n message.value\n ? h('div', { class: 'space-y-2' }, [\n h(\n 'p',\n { class: 'text-lg text-gray-900 dark:text-white break-words' },\n message.value\n ),\n h('div', { class: 'flex gap-4 text-sm' }, [\n h(\n 'div',\n { class: 'text-gray-600 dark:text-gray-400' },\n `${currentWordCount.value} ${\n currentWordCount.value === 1 ? 'word' : 'words'\n }`\n ),\n props.buttonMetrics\n ? [\n h(\n 'div',\n { class: 'text-gray-600 dark:text-gray-400' },\n [\n 'Effort: ',\n h(\n 'span',\n { class: 'font-medium' },\n currentEffort.value.toFixed(2)\n ),\n ]\n ),\n h(\n 'div',\n { class: 'text-gray-600 dark:text-gray-400' },\n [\n 'Avg: ',\n h(\n 'span',\n { class: 'font-medium' },\n currentWordCount.value > 0\n ? (currentEffort.value / currentWordCount.value).toFixed(2)\n : '0.00'\n ),\n ]\n ),\n ]\n : null,\n ]),\n ])\n : h(\n 'p',\n { class: 'text-gray-500 dark:text-gray-400 italic' },\n 'Tap buttons to build a sentence...'\n ),\n ]),\n message.value\n ? h(\n 'button',\n {\n onClick: clearMessage,\n class:\n 'p-2 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-lg transition',\n 'aria-label': 'Clear message',\n },\n [\n h(\n 'svg',\n {\n class: 'h-5 w-5 text-gray-600 dark:text-gray-400',\n fill: 'none',\n stroke: 'currentColor',\n viewBox: '0 0 24 24',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'stroke-width': 2,\n d: 'M6 18L18 6M6 6l12 12',\n }),\n ]\n ),\n ]\n )\n : null,\n ]),\n ]\n )\n : null,\n h('div', { class: 'flex' }, [\n renderToolbar(toolbarPage),\n h('div', { class: 'flex-1' }, [\n h('div', {\n class: 'flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700',\n }, [\n h('div', { class: 'flex items-center gap-2' }, [\n pageHistory.value.length > 0\n ? h(\n 'button',\n {\n onClick: handleBack,\n class: 'p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition',\n 'aria-label': 'Go back',\n },\n [\n h(\n 'svg',\n {\n class: 'h-5 w-5 text-gray-600 dark:text-gray-400',\n fill: 'none',\n stroke: 'currentColor',\n viewBox: '0 0 24 24',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'stroke-width': 2,\n d: 'M15 19l-7-7 7-7',\n }),\n ]\n ),\n ]\n )\n : null,\n h(\n 'h3',\n { class: 'text-lg font-semibold text-gray-900 dark:text-white' },\n currentPage.value.name\n ),\n ]),\n h(\n 'div',\n { class: 'text-sm text-gray-500 dark:text-gray-400' },\n `${gridRows}×${gridCols} grid`\n ),\n ]),\n h(\n 'div',\n {\n class: 'p-4 gap-2 overflow-auto max-h-[600px]',\n style: {\n display: 'grid',\n gridTemplateColumns: `repeat(${gridCols}, minmax(0, 1fr))`,\n },\n },\n renderGridCells()\n ),\n Object.keys(props.tree.pages).length > 1\n ? h('div', { class: 'p-4 border-t border-gray-200 dark:border-gray-700' }, [\n h(\n 'p',\n { class: 'text-sm text-gray-600 dark:text-gray-400 mb-2' },\n `${Object.keys(props.tree.pages).length} pages in this vocabulary`\n ),\n ])\n : null,\n ]),\n ]),\n predictionsTooltip.value\n ? h(PredictionsTooltip, {\n predictions: predictionsTooltip.value.predictions,\n label: predictionsTooltip.value.label,\n position: predictionsTooltip.value.position,\n buttonMetricsLookup: predictionsTooltip.value.buttonMetricsLookup,\n onClose: predictionsTooltip.value.onClose,\n onWordClick: predictionsTooltip.value.onWordClick,\n })\n : null,\n ]);\n };\n },\n});\n\nexport default BoardViewer;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/vue/index.ts","../../src/vue/BoardViewer.ts"],"sourcesContent":["export { BoardViewer } from './BoardViewer';\nexport type { BoardViewerProps, ButtonMetric } from '../types';\nexport type { AACTree, AACPage, AACButton } from '@willwade/aac-processors';\n","import {\n defineComponent,\n ref,\n computed,\n watch,\n onMounted,\n onBeforeUnmount,\n nextTick,\n h,\n type PropType,\n type VNode,\n type VNodeRef,\n} from 'vue';\nimport type { AACPage, AACButton } from '@willwade/aac-processors';\nimport type { BoardViewerProps, ButtonMetric } from '../types';\n\ninterface PredictionsTooltipProps {\n predictions: string[];\n label: string;\n position: { x: number; y: number };\n buttonMetricsLookup: { [buttonId: string]: ButtonMetric };\n onClose: () => void;\n onWordClick?: (word: string, effort: number) => void;\n pos?: string;\n}\n\nconst PredictionsTooltip = defineComponent({\n name: 'PredictionsTooltip',\n props: {\n predictions: { type: Array as PropType<string[]>, required: true },\n label: { type: String, required: true },\n position: {\n type: Object as PropType<{ x: number; y: number }>,\n required: true,\n },\n buttonMetricsLookup: {\n type: Object as PropType<{ [buttonId: string]: ButtonMetric }>,\n required: true,\n },\n onClose: { type: Function as PropType<() => void>, required: true },\n onWordClick: {\n type: Function as PropType<(word: string, effort: number) => void>,\n required: false,\n },\n pos: { type: String, default: undefined },\n },\n setup(props: Readonly<PredictionsTooltipProps>) {\n const handleClickOutside = (e: MouseEvent) => {\n if (e.target instanceof HTMLElement && !e.target.closest('.predictions-tooltip')) {\n props.onClose();\n }\n };\n\n onMounted(() => {\n document.addEventListener('mousedown', handleClickOutside);\n });\n\n onBeforeUnmount(() => {\n document.removeEventListener('mousedown', handleClickOutside);\n });\n\n const getPosition = () => {\n const maxX = typeof window !== 'undefined' ? window.innerWidth - 200 : props.position.x;\n const maxY = typeof window !== 'undefined' ? window.innerHeight - 150 : props.position.y;\n return {\n left: `${Math.min(props.position.x, maxX)}px`,\n top: `${Math.min(props.position.y, maxY)}px`,\n };\n };\n\n return () => {\n const positionStyle = getPosition();\n\n return h(\n 'div',\n {\n class:\n 'predictions-tooltip fixed z-50 bg-white dark:bg-gray-800 rounded-lg shadow-xl border-2 border-purple-500 p-3 max-w-xs',\n style: positionStyle,\n onClick: (e: MouseEvent) => e.stopPropagation(),\n },\n [\n h('div', { class: 'flex items-center justify-between mb-2' }, [\n h(\n 'h4',\n { class: 'text-sm font-semibold text-gray-900 dark:text-white flex items-center gap-1.5' },\n [\n `Word forms for \"${props.label}\"`,\n props.pos && props.pos !== 'Unknown' && props.pos !== 'Ignore'\n ? h(\n 'span',\n {\n class: `px-1.5 py-0 text-[10px] font-semibold rounded text-white ${\n props.pos === 'Verb' ? 'bg-orange-500' :\n props.pos === 'Noun' ? 'bg-teal-500' :\n props.pos === 'Pronoun' ? 'bg-pink-500' :\n props.pos === 'Adjective' ? 'bg-yellow-500' :\n 'bg-gray-500'\n }`,\n },\n props.pos\n )\n : null,\n ]\n ),\n h(\n 'button',\n {\n onClick: props.onClose,\n class: 'p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded',\n 'aria-label': 'Close',\n },\n [\n h(\n 'svg',\n {\n class: 'h-4 w-4 text-gray-600 dark:text-gray-400',\n fill: 'none',\n stroke: 'currentColor',\n viewBox: '0 0 24 24',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'stroke-width': 2,\n d: 'M6 18L18 6M6 6l12 12',\n }),\n ]\n ),\n ]\n ),\n ]),\n h(\n 'div',\n { class: 'flex flex-wrap gap-1' },\n props.predictions.map((word: string, idx: number) => {\n const metricForWord = Object.values(props.buttonMetricsLookup).find(\n (m: ButtonMetric) => m.label === word\n );\n const effort = metricForWord?.effort;\n\n return h(\n 'span',\n {\n key: `${word}-${idx}`,\n class:\n 'px-2 py-1 bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 rounded text-xs font-medium relative cursor-pointer hover:bg-purple-200 dark:hover:bg-purple-800 transition',\n title: `Click to add \"${word}\" to message`,\n onClick: (e: MouseEvent) => {\n e.stopPropagation();\n if (props.onWordClick) {\n props.onWordClick(word, effort || 1.0);\n props.onClose();\n }\n },\n },\n [\n effort !== undefined\n ? h(\n 'span',\n {\n class:\n 'absolute -top-1 -right-1 px-1 py-0 text-[8px] font-semibold rounded bg-blue-600 text-white shadow-xs',\n },\n effort.toFixed(1)\n )\n : null,\n word,\n ]\n );\n })\n ),\n ]\n );\n };\n },\n});\n\nexport const BoardViewer = defineComponent({\n name: 'BoardViewer',\n props: {\n tree: { type: Object as PropType<BoardViewerProps['tree']>, required: true },\n buttonMetrics: { type: Array as PropType<ButtonMetric[] | null>, default: null },\n showMessageBar: { type: Boolean, default: true },\n showEffortBadges: { type: Boolean, default: true },\n showLinkIndicators: { type: Boolean, default: true },\n initialPageId: { type: String, default: undefined },\n navigateToPageId: { type: String, default: undefined },\n highlight: { type: Object as PropType<BoardViewerProps['highlight']>, default: undefined },\n onButtonClick: {\n type: Function as PropType<(button: AACButton) => void>,\n default: undefined,\n },\n onPageChange: {\n type: Function as PropType<(pageId: string) => void>,\n default: undefined,\n },\n className: { type: String, default: '' },\n loadId: { type: String, default: undefined },\n },\n setup(props: Readonly<BoardViewerProps>) {\n const resolveInitialPageId = () => {\n if (props.initialPageId && props.tree.pages[props.initialPageId]) {\n return props.initialPageId;\n }\n if (props.tree.toolbarId && props.tree.rootId && props.tree.rootId !== props.tree.toolbarId) {\n return props.tree.rootId;\n }\n if (props.tree.rootId && props.tree.pages[props.tree.rootId]) {\n const rootPage = props.tree.pages[props.tree.rootId];\n const isToolbar =\n rootPage.name.toLowerCase().includes('toolbar') ||\n rootPage.name.toLowerCase().includes('tool bar');\n if (!isToolbar) {\n return props.tree.rootId;\n }\n }\n const startPage = Object.values(props.tree.pages).find(\n (p) => p.name.toLowerCase() === 'start'\n );\n if (startPage) {\n return startPage.id;\n }\n const nonToolbarPage = Object.values(props.tree.pages).find(\n (p) => !p.name.toLowerCase().includes('toolbar') && !p.name.toLowerCase().includes('tool bar')\n );\n if (nonToolbarPage) {\n return nonToolbarPage.id;\n }\n const pageIds = Object.keys(props.tree.pages);\n return pageIds.length > 0 ? pageIds[0] : null;\n };\n\n const currentPageId = ref<string | null>(resolveInitialPageId());\n const highlightedButtonRef = ref<HTMLButtonElement | null>(null);\n const pageHistory = ref<AACPage[]>([]);\n const message = ref('');\n const currentWordCount = ref(0);\n const currentEffort = ref(0);\n\n const predictionsTooltip = ref<PredictionsTooltipProps | null>(null);\n\n const buttonMetricsLookup = computed(() => {\n if (!props.buttonMetrics) return {};\n const lookup: { [buttonId: string]: ButtonMetric } = {};\n props.buttonMetrics.forEach((metric: ButtonMetric) => {\n lookup[metric.id] = metric;\n });\n return lookup;\n });\n\n const currentPage = computed(() =>\n currentPageId.value ? props.tree.pages[currentPageId.value] : null\n );\n\n const goToPage = (targetPageId: string | undefined | null) => {\n if (!targetPageId || !props.tree.pages[targetPageId]) return false;\n if (currentPage.value) {\n pageHistory.value = [...pageHistory.value, currentPage.value];\n }\n currentPageId.value = targetPageId;\n if (props.onPageChange) {\n props.onPageChange(targetPageId);\n }\n return true;\n };\n\n watch([() => props.tree, () => props.initialPageId], () => {\n currentPageId.value = resolveInitialPageId();\n pageHistory.value = [];\n });\n\n watch(\n () => props.navigateToPageId,\n (pageId: string | undefined) => {\n if (!pageId) return;\n if (!props.tree.pages[pageId]) return;\n currentPageId.value = pageId;\n pageHistory.value = [];\n if (props.onPageChange) {\n props.onPageChange(pageId);\n }\n }\n );\n\n watch(\n [\n currentPageId,\n () => props.highlight?.pageId,\n () => props.highlight?.x,\n () => props.highlight?.y,\n () => props.highlight?.label,\n ],\n () => {\n nextTick(() => {\n if (highlightedButtonRef.value) {\n highlightedButtonRef.value.scrollIntoView({\n behavior: 'smooth',\n block: 'center',\n inline: 'center',\n });\n }\n });\n }\n );\n\n const updateStats = (word: string, effort: number) => {\n currentWordCount.value += 1;\n currentEffort.value += effort;\n };\n\n const clearMessage = () => {\n message.value = '';\n currentWordCount.value = 0;\n currentEffort.value = 0;\n };\n\n const handleBack = () => {\n if (pageHistory.value.length > 0) {\n const previousPage = pageHistory.value[pageHistory.value.length - 1];\n pageHistory.value = pageHistory.value.slice(0, -1);\n currentPageId.value = previousPage.id;\n if (props.onPageChange) {\n props.onPageChange(previousPage.id);\n }\n }\n };\n\n const handleButtonClick = (button: AACButton) => {\n if (props.onButtonClick) {\n props.onButtonClick(button);\n }\n\n const intent = button.semanticAction?.intent\n ? String(button.semanticAction.intent)\n : undefined;\n const targetPageId = button.targetPageId || button.semanticAction?.targetId;\n const effort = buttonMetricsLookup.value[button.id]?.effort || 1;\n const textValue =\n button.semanticAction?.text || button.message || button.label || '';\n\n const deleteLastWord = () => {\n message.value = message.value\n .trim()\n .split(/\\s+/)\n .slice(0, -1)\n .join(' ');\n };\n\n const deleteLastCharacter = () => {\n message.value = message.value.slice(0, -1);\n };\n\n const appendText = (word: string) => {\n const trimmed = word || button.label || '';\n message.value = trimmed\n ? `${message.value}${message.value ? ' ' : ''}${trimmed}`\n : message.value;\n if (trimmed) {\n updateStats(trimmed, effort);\n }\n };\n\n if (intent === 'NAVIGATE_TO' && goToPage(targetPageId)) {\n return;\n }\n\n switch (intent) {\n case 'GO_BACK':\n handleBack();\n return;\n case 'GO_HOME':\n if (props.tree.rootId && goToPage(props.tree.rootId)) return;\n break;\n case 'DELETE_WORD':\n deleteLastWord();\n return;\n case 'DELETE_CHARACTER':\n deleteLastCharacter();\n return;\n case 'CLEAR_TEXT':\n clearMessage();\n return;\n case 'SPEAK_IMMEDIATE':\n case 'SPEAK_TEXT':\n case 'INSERT_TEXT':\n appendText(textValue);\n return;\n default:\n break;\n }\n\n if (targetPageId && goToPage(targetPageId)) {\n return;\n }\n\n appendText(textValue);\n };\n\n const handleShowPredictions = (button: AACButton, event: MouseEvent) => {\n event.stopPropagation();\n const predictions =\n button.predictions || (button.parameters as { predictions?: string[] })?.predictions;\n const buttonMetric = buttonMetricsLookup.value[button.id];\n if (predictions && predictions.length > 0) {\n predictionsTooltip.value = {\n predictions,\n label: button.label,\n position: { x: event.clientX, y: event.clientY },\n buttonMetricsLookup: buttonMetricsLookup.value,\n pos: buttonMetric?.pos || button.pos,\n onWordClick: (word: string, effort: number) => {\n const trimmed = word || '';\n if (trimmed) {\n message.value = `${message.value}${message.value ? ' ' : ''}${trimmed}`;\n updateStats(trimmed, effort);\n }\n },\n onClose: () => {\n predictionsTooltip.value = null;\n },\n };\n }\n };\n\n const getTextColor = (backgroundColor?: string) => {\n if (!backgroundColor) return '#111827';\n\n const hex = backgroundColor.replace('#', '');\n if (hex.length === 6) {\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\n return brightness >= 128 ? '#111827' : '#f9fafb';\n }\n\n return '#111827';\n };\n\n const renderToolbar = (toolbarPage: AACPage | null): VNode | null => {\n if (!toolbarPage) return null;\n\n return h('div', {\n class:\n 'w-16 sm:w-20 border-r border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/50',\n }, [\n h('div', { class: 'p-2' }, [\n h(\n 'p',\n {\n class:\n 'text-[10px] text-gray-500 dark:text-gray-400 text-center mb-2 font-semibold',\n },\n 'TOOLBAR'\n ),\n h(\n 'div',\n { class: 'grid gap-1' },\n toolbarPage.grid.flatMap((row: (AACButton | null)[]) =>\n row.map((button: AACButton | null) => {\n if (!button) return null;\n\n const buttonMetric = buttonMetricsLookup.value[button.id];\n const effort = buttonMetric?.effort || 0;\n\n return h(\n 'button',\n {\n key: button.id,\n onClick: () => handleButtonClick(button),\n class:\n 'aspect-square p-1 rounded border border-gray-200 dark:border-gray-700 transition flex flex-col items-center justify-center gap-0.5 hover:bg-gray-200 dark:hover:bg-gray-700 relative',\n style: {\n backgroundColor: button.style?.backgroundColor || '#f3f4f6',\n borderColor: button.style?.borderColor || '#e5e7eb',\n color:\n button.style?.fontColor || getTextColor(button.style?.backgroundColor),\n },\n title: `${button.label}\\n${button.message || ''}`,\n },\n [\n buttonMetric && props.showEffortBadges && effort > 0\n ? h(\n 'div',\n {\n class:\n 'absolute top-0 right-0 px-0.5 py-0 text-[8px] font-semibold rounded bg-blue-600 text-white',\n },\n effort.toFixed(1)\n )\n : null,\n h(\n 'span',\n {\n class:\n 'text-[8px] sm:text-[9px] text-center font-medium leading-tight line-clamp-2',\n },\n button.label\n ),\n ]\n );\n })\n )\n ),\n ]),\n ]);\n };\n\n const renderGridCells = (): (VNode | null)[] => {\n if (!currentPage.value) return [];\n const rendered = new Set<string>();\n const cells: (VNode | null)[] = [];\n\n currentPage.value.grid.forEach((row: (AACButton | null)[], rowIndex: number) => {\n row.forEach((button: AACButton | null, colIndex: number) => {\n if (!button) {\n cells.push(\n h('div', {\n key: `empty-${rowIndex}-${colIndex}`,\n class: 'aspect-square',\n })\n );\n return;\n }\n\n if (rendered.has(button.id)) {\n cells.push(null);\n return;\n }\n rendered.add(button.id);\n\n const buttonMetric = buttonMetricsLookup.value[button.id];\n const effort = buttonMetric?.effort || 0;\n const targetPageId = button.targetPageId || button.semanticAction?.targetId;\n const hasLink = targetPageId && props.tree.pages[targetPageId];\n const colSpan = button.columnSpan || 1;\n const rowSpan = button.rowSpan || 1;\n const predictions =\n button.predictions || (button.parameters as { predictions?: string[] })?.predictions;\n const hasPredictions = predictions && predictions.length > 0;\n const isPredictionCell =\n button.contentType === 'AutoContent' &&\n (button.contentSubType || '').toLowerCase() === 'prediction';\n const isWorkspace = button.contentType === 'Workspace';\n\n const isHighlighted =\n props.highlight &&\n props.highlight.pageId === currentPageId.value &&\n (props.highlight.buttonId === button.id ||\n (props.highlight.x !== undefined && props.highlight.y !== undefined\n ? button.x === props.highlight.x && button.y === props.highlight.y\n : false) ||\n (props.highlight.label && button.label === props.highlight.label));\n\n const params = button.parameters as {\n image_id?: string;\n gridPageName?: string;\n imageData?: Buffer;\n };\n\n let imageSrc: string | null = null;\n let apiUrl: string | undefined = undefined;\n\n const resolvedEntry = button.resolvedImageEntry;\n const buttonImage = button.image;\n\n if (\n resolvedEntry &&\n typeof resolvedEntry === 'string' &&\n resolvedEntry.startsWith('data:image/')\n ) {\n imageSrc = resolvedEntry;\n } else if (\n buttonImage &&\n typeof buttonImage === 'string' &&\n buttonImage.startsWith('data:image/')\n ) {\n imageSrc = buttonImage;\n } else if (\n resolvedEntry &&\n typeof resolvedEntry === 'string' &&\n !resolvedEntry.startsWith('[') &&\n !resolvedEntry.startsWith('data:image/')\n ) {\n if (props.loadId) {\n const entryPath = resolvedEntry;\n const pathMatch = entryPath.match(/^(?:Grids\\/)?(.+)$/);\n if (pathMatch) {\n apiUrl = `/api/image/${props.loadId}/${encodeURIComponent(pathMatch[1])}`;\n }\n } else {\n imageSrc = resolvedEntry;\n }\n } else if (\n buttonImage &&\n typeof buttonImage === 'string' &&\n !buttonImage.startsWith('[')\n ) {\n imageSrc = buttonImage;\n }\n\n if (\n props.loadId &&\n !imageSrc &&\n !apiUrl &&\n params &&\n params.image_id &&\n typeof params.image_id === 'string'\n ) {\n apiUrl = `/api/image/${props.loadId}/${encodeURIComponent(params.image_id)}`;\n }\n\n if (isWorkspace) {\n cells.push(\n h(\n 'div',\n {\n key: button.id,\n class:\n 'relative p-3 rounded-lg border-2 bg-gray-50 text-gray-800 flex items-center gap-2',\n style: {\n borderColor: button.style?.borderColor || '#e5e7eb',\n gridColumn: `${colIndex + 1} / span ${colSpan}`,\n gridRow: `${rowIndex + 1} / span ${rowSpan}`,\n },\n },\n [\n h('div', { class: 'font-semibold text-sm' }, button.label || 'Workspace'),\n h('div', { class: 'text-xs text-gray-500 truncate' }, message.value || 'Chat writing area'),\n ]\n )\n );\n return;\n }\n\n cells.push(\n h(\n 'button',\n {\n key: button.id,\n onClick: () => handleButtonClick(button),\n ref: isHighlighted\n ? ((el: Element | null) => {\n highlightedButtonRef.value = el as HTMLButtonElement | null;\n }) as VNodeRef\n : undefined,\n class:\n `relative aspect-square p-2 rounded-lg border-2 transition flex flex-col items-center justify-center gap-1 hover:opacity-80 hover:scale-105 active:scale-95 ${\n isHighlighted ? 'ring-4 ring-amber-400 ring-offset-2 ring-offset-white' : ''\n }`,\n style: {\n backgroundColor: button.style?.backgroundColor || '#f3f4f6',\n borderColor: button.style?.borderColor || '#e5e7eb',\n color:\n button.style?.fontColor || getTextColor(button.style?.backgroundColor),\n gridColumn: `${colIndex + 1} / span ${colSpan}`,\n gridRow: `${rowIndex + 1} / span ${rowSpan}`,\n },\n },\n [\n buttonMetric && props.showEffortBadges\n ? h(\n 'div',\n {\n class:\n 'absolute top-1 right-1 px-1.5 py-0.5 text-xs font-semibold rounded bg-blue-600 text-white shadow-sm',\n },\n effort.toFixed(1)\n )\n : null,\n hasLink && props.showLinkIndicators\n ? h('div', {\n class:\n 'absolute top-1 left-1 w-2 h-2 bg-green-500 rounded-full shadow-sm',\n })\n : null,\n buttonMetric?.pos && buttonMetric.pos !== 'Unknown' && buttonMetric.pos !== 'Ignore'\n ? h('div', {\n class: `absolute top-1 ${hasLink && props.showLinkIndicators ? 'left-4' : 'left-1'} px-1 py-0 text-[8px] font-semibold rounded shadow-sm text-white ${\n buttonMetric.pos === 'Verb' ? 'bg-orange-500' :\n buttonMetric.pos === 'Noun' ? 'bg-teal-500' :\n buttonMetric.pos === 'Pronoun' ? 'bg-pink-500' :\n buttonMetric.pos === 'Adjective' ? 'bg-yellow-500' :\n 'bg-gray-500'\n }`,\n title: `Part of speech: ${buttonMetric.pos}`,\n }, buttonMetric.pos.charAt(0))\n : null,\n hasPredictions\n ? h(\n 'div',\n {\n onClick: (event: MouseEvent) => handleShowPredictions(button, event),\n class:\n 'absolute bottom-1 right-1 px-1.5 py-0.5 text-xs font-semibold rounded bg-purple-600 text-white shadow-sm cursor-pointer hover:bg-purple-700 transition',\n title: `Has ${predictions?.length} word form${\n predictions && predictions.length > 1 ? 's' : ''\n }`,\n },\n predictions?.length\n )\n : null,\n imageSrc || apiUrl\n ? h('img', {\n src: imageSrc || apiUrl,\n alt: button.label,\n class: 'max-h-12 object-contain',\n onError: (e: Event) => {\n const target = e.target as HTMLImageElement;\n console.warn('Image failed to load:', button.label, 'src:', target.src);\n target.style.display = 'none';\n },\n })\n : null,\n h('div', { class: 'flex flex-col items-center justify-center' }, [\n h(\n 'span',\n {\n class: 'text-xs sm:text-sm text-center font-medium leading-tight line-clamp-3',\n },\n button.label\n ),\n isPredictionCell && predictions && predictions.length > 0\n ? h(\n 'div',\n {\n class:\n 'mt-1 text-[10px] sm:text-xs text-center opacity-80 space-y-0.5',\n },\n [\n ...predictions.slice(0, 3).map((prediction: string, idx: number) =>\n h('div', { key: `${button.id}-pred-${idx}` }, prediction)\n ),\n predictions.length > 3 ? h('div', '…') : null,\n ]\n )\n : null,\n ]),\n button.message && button.message !== button.label && !isPredictionCell\n ? h(\n 'span',\n { class: 'text-[10px] sm:text-xs text-center opacity-75 line-clamp-2' },\n button.message\n )\n : null,\n ]\n )\n );\n });\n });\n\n return cells;\n };\n\n return () => {\n if (!currentPage.value) {\n return h(\n 'div',\n {\n class: `flex items-center justify-center h-96 bg-gray-100 dark:bg-gray-800 rounded-lg ${\n props.className\n }`,\n },\n [\n h('div', { class: 'text-center' }, [\n h('p', { class: 'text-gray-600 dark:text-gray-400' }, 'No pages available'),\n ]),\n ]\n );\n }\n\n const toolbarPage = props.tree.toolbarId ? props.tree.pages[props.tree.toolbarId] : null;\n const gridRows = currentPage.value.grid.length;\n const gridCols = gridRows > 0 ? currentPage.value.grid[0].length : 0;\n\n return h('div', {\n class: `bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden ${props.className}`,\n }, [\n props.showMessageBar\n ? h(\n 'div',\n {\n class:\n 'p-4 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/50',\n },\n [\n h('div', { class: 'flex items-start justify-between gap-4' }, [\n h('div', { class: 'flex-1 min-w-0' }, [\n message.value\n ? h('div', { class: 'space-y-2' }, [\n h(\n 'p',\n { class: 'text-lg text-gray-900 dark:text-white break-words' },\n message.value\n ),\n h('div', { class: 'flex gap-4 text-sm' }, [\n h(\n 'div',\n { class: 'text-gray-600 dark:text-gray-400' },\n `${currentWordCount.value} ${\n currentWordCount.value === 1 ? 'word' : 'words'\n }`\n ),\n props.buttonMetrics\n ? [\n h(\n 'div',\n { class: 'text-gray-600 dark:text-gray-400' },\n [\n 'Effort: ',\n h(\n 'span',\n { class: 'font-medium' },\n currentEffort.value.toFixed(2)\n ),\n ]\n ),\n h(\n 'div',\n { class: 'text-gray-600 dark:text-gray-400' },\n [\n 'Avg: ',\n h(\n 'span',\n { class: 'font-medium' },\n currentWordCount.value > 0\n ? (currentEffort.value / currentWordCount.value).toFixed(2)\n : '0.00'\n ),\n ]\n ),\n ]\n : null,\n ]),\n ])\n : h(\n 'p',\n { class: 'text-gray-500 dark:text-gray-400 italic' },\n 'Tap buttons to build a sentence...'\n ),\n ]),\n message.value\n ? h(\n 'button',\n {\n onClick: clearMessage,\n class:\n 'p-2 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-lg transition',\n 'aria-label': 'Clear message',\n },\n [\n h(\n 'svg',\n {\n class: 'h-5 w-5 text-gray-600 dark:text-gray-400',\n fill: 'none',\n stroke: 'currentColor',\n viewBox: '0 0 24 24',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'stroke-width': 2,\n d: 'M6 18L18 6M6 6l12 12',\n }),\n ]\n ),\n ]\n )\n : null,\n ]),\n ]\n )\n : null,\n h('div', { class: 'flex' }, [\n renderToolbar(toolbarPage),\n h('div', { class: 'flex-1' }, [\n h('div', {\n class: 'flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700',\n }, [\n h('div', { class: 'flex items-center gap-2' }, [\n pageHistory.value.length > 0\n ? h(\n 'button',\n {\n onClick: handleBack,\n class: 'p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition',\n 'aria-label': 'Go back',\n },\n [\n h(\n 'svg',\n {\n class: 'h-5 w-5 text-gray-600 dark:text-gray-400',\n fill: 'none',\n stroke: 'currentColor',\n viewBox: '0 0 24 24',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'stroke-width': 2,\n d: 'M15 19l-7-7 7-7',\n }),\n ]\n ),\n ]\n )\n : null,\n h(\n 'h3',\n { class: 'text-lg font-semibold text-gray-900 dark:text-white' },\n currentPage.value.name\n ),\n ]),\n h(\n 'div',\n { class: 'text-sm text-gray-500 dark:text-gray-400' },\n `${gridRows}×${gridCols} grid`\n ),\n ]),\n h(\n 'div',\n {\n class: 'p-4 gap-2 overflow-auto max-h-[600px]',\n style: {\n display: 'grid',\n gridTemplateColumns: `repeat(${gridCols}, minmax(0, 1fr))`,\n },\n },\n renderGridCells()\n ),\n Object.keys(props.tree.pages).length > 1\n ? h('div', { class: 'p-4 border-t border-gray-200 dark:border-gray-700' }, [\n h(\n 'p',\n { class: 'text-sm text-gray-600 dark:text-gray-400 mb-2' },\n `${Object.keys(props.tree.pages).length} pages in this vocabulary`\n ),\n ])\n : null,\n ]),\n ]),\n predictionsTooltip.value\n ? h(PredictionsTooltip, {\n predictions: predictionsTooltip.value.predictions,\n label: predictionsTooltip.value.label,\n position: predictionsTooltip.value.position,\n buttonMetricsLookup: predictionsTooltip.value.buttonMetricsLookup,\n onClose: predictionsTooltip.value.onClose,\n onWordClick: predictionsTooltip.value.onWordClick,\n pos: predictionsTooltip.value.pos,\n })\n : null,\n ]);\n };\n },\n});\n\nexport default BoardViewer;\n"],"mappings":"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","names":[]}
@@ -26,7 +26,8 @@ var PredictionsTooltip = defineComponent({
26
26
  onWordClick: {
27
27
  type: Function,
28
28
  required: false
29
- }
29
+ },
30
+ pos: { type: String, default: void 0 }
30
31
  },
31
32
  setup(props) {
32
33
  const handleClickOutside = (e) => {
@@ -61,8 +62,17 @@ var PredictionsTooltip = defineComponent({
61
62
  h("div", { class: "flex items-center justify-between mb-2" }, [
62
63
  h(
63
64
  "h4",
64
- { class: "text-sm font-semibold text-gray-900 dark:text-white" },
65
- `Word forms for "${props.label}"`
65
+ { class: "text-sm font-semibold text-gray-900 dark:text-white flex items-center gap-1.5" },
66
+ [
67
+ `Word forms for "${props.label}"`,
68
+ props.pos && props.pos !== "Unknown" && props.pos !== "Ignore" ? h(
69
+ "span",
70
+ {
71
+ class: `px-1.5 py-0 text-[10px] font-semibold rounded text-white ${props.pos === "Verb" ? "bg-orange-500" : props.pos === "Noun" ? "bg-teal-500" : props.pos === "Pronoun" ? "bg-pink-500" : props.pos === "Adjective" ? "bg-yellow-500" : "bg-gray-500"}`
72
+ },
73
+ props.pos
74
+ ) : null
75
+ ]
66
76
  ),
67
77
  h(
68
78
  "button",
@@ -324,12 +334,14 @@ var BoardViewer = defineComponent({
324
334
  const handleShowPredictions = (button, event) => {
325
335
  event.stopPropagation();
326
336
  const predictions = button.predictions || button.parameters?.predictions;
337
+ const buttonMetric = buttonMetricsLookup.value[button.id];
327
338
  if (predictions && predictions.length > 0) {
328
339
  predictionsTooltip.value = {
329
340
  predictions,
330
341
  label: button.label,
331
342
  position: { x: event.clientX, y: event.clientY },
332
343
  buttonMetricsLookup: buttonMetricsLookup.value,
344
+ pos: buttonMetric?.pos || button.pos,
333
345
  onWordClick: (word, effort) => {
334
346
  const trimmed = word || "";
335
347
  if (trimmed) {
@@ -519,6 +531,10 @@ ${button.message || ""}`
519
531
  hasLink && props.showLinkIndicators ? h("div", {
520
532
  class: "absolute top-1 left-1 w-2 h-2 bg-green-500 rounded-full shadow-sm"
521
533
  }) : null,
534
+ buttonMetric?.pos && buttonMetric.pos !== "Unknown" && buttonMetric.pos !== "Ignore" ? h("div", {
535
+ class: `absolute top-1 ${hasLink && props.showLinkIndicators ? "left-4" : "left-1"} px-1 py-0 text-[8px] font-semibold rounded shadow-sm text-white ${buttonMetric.pos === "Verb" ? "bg-orange-500" : buttonMetric.pos === "Noun" ? "bg-teal-500" : buttonMetric.pos === "Pronoun" ? "bg-pink-500" : buttonMetric.pos === "Adjective" ? "bg-yellow-500" : "bg-gray-500"}`,
536
+ title: `Part of speech: ${buttonMetric.pos}`
537
+ }, buttonMetric.pos.charAt(0)) : null,
522
538
  hasPredictions ? h(
523
539
  "div",
524
540
  {
@@ -746,7 +762,8 @@ ${button.message || ""}`
746
762
  position: predictionsTooltip.value.position,
747
763
  buttonMetricsLookup: predictionsTooltip.value.buttonMetricsLookup,
748
764
  onClose: predictionsTooltip.value.onClose,
749
- onWordClick: predictionsTooltip.value.onWordClick
765
+ onWordClick: predictionsTooltip.value.onWordClick,
766
+ pos: predictionsTooltip.value.pos
750
767
  }) : null
751
768
  ]);
752
769
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/vue/BoardViewer.ts"],"sourcesContent":["import {\n defineComponent,\n ref,\n computed,\n watch,\n onMounted,\n onBeforeUnmount,\n nextTick,\n h,\n type PropType,\n type VNode,\n type VNodeRef,\n} from 'vue';\nimport type { AACPage, AACButton } from '@willwade/aac-processors';\nimport type { BoardViewerProps, ButtonMetric } from '../types';\n\ninterface PredictionsTooltipProps {\n predictions: string[];\n label: string;\n position: { x: number; y: number };\n buttonMetricsLookup: { [buttonId: string]: ButtonMetric };\n onClose: () => void;\n onWordClick?: (word: string, effort: number) => void;\n}\n\nconst PredictionsTooltip = defineComponent({\n name: 'PredictionsTooltip',\n props: {\n predictions: { type: Array as PropType<string[]>, required: true },\n label: { type: String, required: true },\n position: {\n type: Object as PropType<{ x: number; y: number }>,\n required: true,\n },\n buttonMetricsLookup: {\n type: Object as PropType<{ [buttonId: string]: ButtonMetric }>,\n required: true,\n },\n onClose: { type: Function as PropType<() => void>, required: true },\n onWordClick: {\n type: Function as PropType<(word: string, effort: number) => void>,\n required: false,\n },\n },\n setup(props: Readonly<PredictionsTooltipProps>) {\n const handleClickOutside = (e: MouseEvent) => {\n if (e.target instanceof HTMLElement && !e.target.closest('.predictions-tooltip')) {\n props.onClose();\n }\n };\n\n onMounted(() => {\n document.addEventListener('mousedown', handleClickOutside);\n });\n\n onBeforeUnmount(() => {\n document.removeEventListener('mousedown', handleClickOutside);\n });\n\n const getPosition = () => {\n const maxX = typeof window !== 'undefined' ? window.innerWidth - 200 : props.position.x;\n const maxY = typeof window !== 'undefined' ? window.innerHeight - 150 : props.position.y;\n return {\n left: `${Math.min(props.position.x, maxX)}px`,\n top: `${Math.min(props.position.y, maxY)}px`,\n };\n };\n\n return () => {\n const positionStyle = getPosition();\n\n return h(\n 'div',\n {\n class:\n 'predictions-tooltip fixed z-50 bg-white dark:bg-gray-800 rounded-lg shadow-xl border-2 border-purple-500 p-3 max-w-xs',\n style: positionStyle,\n onClick: (e: MouseEvent) => e.stopPropagation(),\n },\n [\n h('div', { class: 'flex items-center justify-between mb-2' }, [\n h(\n 'h4',\n { class: 'text-sm font-semibold text-gray-900 dark:text-white' },\n `Word forms for \"${props.label}\"`\n ),\n h(\n 'button',\n {\n onClick: props.onClose,\n class: 'p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded',\n 'aria-label': 'Close',\n },\n [\n h(\n 'svg',\n {\n class: 'h-4 w-4 text-gray-600 dark:text-gray-400',\n fill: 'none',\n stroke: 'currentColor',\n viewBox: '0 0 24 24',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'stroke-width': 2,\n d: 'M6 18L18 6M6 6l12 12',\n }),\n ]\n ),\n ]\n ),\n ]),\n h(\n 'div',\n { class: 'flex flex-wrap gap-1' },\n props.predictions.map((word: string, idx: number) => {\n const metricForWord = Object.values(props.buttonMetricsLookup).find(\n (m: ButtonMetric) => m.label === word\n );\n const effort = metricForWord?.effort;\n\n return h(\n 'span',\n {\n key: `${word}-${idx}`,\n class:\n 'px-2 py-1 bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 rounded text-xs font-medium relative cursor-pointer hover:bg-purple-200 dark:hover:bg-purple-800 transition',\n title: `Click to add \"${word}\" to message`,\n onClick: (e: MouseEvent) => {\n e.stopPropagation();\n if (props.onWordClick) {\n props.onWordClick(word, effort || 1.0);\n props.onClose();\n }\n },\n },\n [\n effort !== undefined\n ? h(\n 'span',\n {\n class:\n 'absolute -top-1 -right-1 px-1 py-0 text-[8px] font-semibold rounded bg-blue-600 text-white shadow-xs',\n },\n effort.toFixed(1)\n )\n : null,\n word,\n ]\n );\n })\n ),\n ]\n );\n };\n },\n});\n\nexport const BoardViewer = defineComponent({\n name: 'BoardViewer',\n props: {\n tree: { type: Object as PropType<BoardViewerProps['tree']>, required: true },\n buttonMetrics: { type: Array as PropType<ButtonMetric[] | null>, default: null },\n showMessageBar: { type: Boolean, default: true },\n showEffortBadges: { type: Boolean, default: true },\n showLinkIndicators: { type: Boolean, default: true },\n initialPageId: { type: String, default: undefined },\n navigateToPageId: { type: String, default: undefined },\n highlight: { type: Object as PropType<BoardViewerProps['highlight']>, default: undefined },\n onButtonClick: {\n type: Function as PropType<(button: AACButton) => void>,\n default: undefined,\n },\n onPageChange: {\n type: Function as PropType<(pageId: string) => void>,\n default: undefined,\n },\n className: { type: String, default: '' },\n loadId: { type: String, default: undefined },\n },\n setup(props: Readonly<BoardViewerProps>) {\n const resolveInitialPageId = () => {\n if (props.initialPageId && props.tree.pages[props.initialPageId]) {\n return props.initialPageId;\n }\n if (props.tree.toolbarId && props.tree.rootId && props.tree.rootId !== props.tree.toolbarId) {\n return props.tree.rootId;\n }\n if (props.tree.rootId && props.tree.pages[props.tree.rootId]) {\n const rootPage = props.tree.pages[props.tree.rootId];\n const isToolbar =\n rootPage.name.toLowerCase().includes('toolbar') ||\n rootPage.name.toLowerCase().includes('tool bar');\n if (!isToolbar) {\n return props.tree.rootId;\n }\n }\n const startPage = Object.values(props.tree.pages).find(\n (p) => p.name.toLowerCase() === 'start'\n );\n if (startPage) {\n return startPage.id;\n }\n const nonToolbarPage = Object.values(props.tree.pages).find(\n (p) => !p.name.toLowerCase().includes('toolbar') && !p.name.toLowerCase().includes('tool bar')\n );\n if (nonToolbarPage) {\n return nonToolbarPage.id;\n }\n const pageIds = Object.keys(props.tree.pages);\n return pageIds.length > 0 ? pageIds[0] : null;\n };\n\n const currentPageId = ref<string | null>(resolveInitialPageId());\n const highlightedButtonRef = ref<HTMLButtonElement | null>(null);\n const pageHistory = ref<AACPage[]>([]);\n const message = ref('');\n const currentWordCount = ref(0);\n const currentEffort = ref(0);\n\n const predictionsTooltip = ref<PredictionsTooltipProps | null>(null);\n\n const buttonMetricsLookup = computed(() => {\n if (!props.buttonMetrics) return {};\n const lookup: { [buttonId: string]: ButtonMetric } = {};\n props.buttonMetrics.forEach((metric: ButtonMetric) => {\n lookup[metric.id] = metric;\n });\n return lookup;\n });\n\n const currentPage = computed(() =>\n currentPageId.value ? props.tree.pages[currentPageId.value] : null\n );\n\n const goToPage = (targetPageId: string | undefined | null) => {\n if (!targetPageId || !props.tree.pages[targetPageId]) return false;\n if (currentPage.value) {\n pageHistory.value = [...pageHistory.value, currentPage.value];\n }\n currentPageId.value = targetPageId;\n if (props.onPageChange) {\n props.onPageChange(targetPageId);\n }\n return true;\n };\n\n watch([() => props.tree, () => props.initialPageId], () => {\n currentPageId.value = resolveInitialPageId();\n pageHistory.value = [];\n });\n\n watch(\n () => props.navigateToPageId,\n (pageId: string | undefined) => {\n if (!pageId) return;\n if (!props.tree.pages[pageId]) return;\n currentPageId.value = pageId;\n pageHistory.value = [];\n if (props.onPageChange) {\n props.onPageChange(pageId);\n }\n }\n );\n\n watch(\n [\n currentPageId,\n () => props.highlight?.pageId,\n () => props.highlight?.x,\n () => props.highlight?.y,\n () => props.highlight?.label,\n ],\n () => {\n nextTick(() => {\n if (highlightedButtonRef.value) {\n highlightedButtonRef.value.scrollIntoView({\n behavior: 'smooth',\n block: 'center',\n inline: 'center',\n });\n }\n });\n }\n );\n\n const updateStats = (word: string, effort: number) => {\n currentWordCount.value += 1;\n currentEffort.value += effort;\n };\n\n const clearMessage = () => {\n message.value = '';\n currentWordCount.value = 0;\n currentEffort.value = 0;\n };\n\n const handleBack = () => {\n if (pageHistory.value.length > 0) {\n const previousPage = pageHistory.value[pageHistory.value.length - 1];\n pageHistory.value = pageHistory.value.slice(0, -1);\n currentPageId.value = previousPage.id;\n if (props.onPageChange) {\n props.onPageChange(previousPage.id);\n }\n }\n };\n\n const handleButtonClick = (button: AACButton) => {\n if (props.onButtonClick) {\n props.onButtonClick(button);\n }\n\n const intent = button.semanticAction?.intent\n ? String(button.semanticAction.intent)\n : undefined;\n const targetPageId = button.targetPageId || button.semanticAction?.targetId;\n const effort = buttonMetricsLookup.value[button.id]?.effort || 1;\n const textValue =\n button.semanticAction?.text || button.message || button.label || '';\n\n const deleteLastWord = () => {\n message.value = message.value\n .trim()\n .split(/\\s+/)\n .slice(0, -1)\n .join(' ');\n };\n\n const deleteLastCharacter = () => {\n message.value = message.value.slice(0, -1);\n };\n\n const appendText = (word: string) => {\n const trimmed = word || button.label || '';\n message.value = trimmed\n ? `${message.value}${message.value ? ' ' : ''}${trimmed}`\n : message.value;\n if (trimmed) {\n updateStats(trimmed, effort);\n }\n };\n\n if (intent === 'NAVIGATE_TO' && goToPage(targetPageId)) {\n return;\n }\n\n switch (intent) {\n case 'GO_BACK':\n handleBack();\n return;\n case 'GO_HOME':\n if (props.tree.rootId && goToPage(props.tree.rootId)) return;\n break;\n case 'DELETE_WORD':\n deleteLastWord();\n return;\n case 'DELETE_CHARACTER':\n deleteLastCharacter();\n return;\n case 'CLEAR_TEXT':\n clearMessage();\n return;\n case 'SPEAK_IMMEDIATE':\n case 'SPEAK_TEXT':\n case 'INSERT_TEXT':\n appendText(textValue);\n return;\n default:\n break;\n }\n\n if (targetPageId && goToPage(targetPageId)) {\n return;\n }\n\n appendText(textValue);\n };\n\n const handleShowPredictions = (button: AACButton, event: MouseEvent) => {\n event.stopPropagation();\n const predictions =\n button.predictions || (button.parameters as { predictions?: string[] })?.predictions;\n if (predictions && predictions.length > 0) {\n predictionsTooltip.value = {\n predictions,\n label: button.label,\n position: { x: event.clientX, y: event.clientY },\n buttonMetricsLookup: buttonMetricsLookup.value,\n onWordClick: (word: string, effort: number) => {\n const trimmed = word || '';\n if (trimmed) {\n message.value = `${message.value}${message.value ? ' ' : ''}${trimmed}`;\n updateStats(trimmed, effort);\n }\n },\n onClose: () => {\n predictionsTooltip.value = null;\n },\n };\n }\n };\n\n const getTextColor = (backgroundColor?: string) => {\n if (!backgroundColor) return '#111827';\n\n const hex = backgroundColor.replace('#', '');\n if (hex.length === 6) {\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\n return brightness >= 128 ? '#111827' : '#f9fafb';\n }\n\n return '#111827';\n };\n\n const renderToolbar = (toolbarPage: AACPage | null): VNode | null => {\n if (!toolbarPage) return null;\n\n return h('div', {\n class:\n 'w-16 sm:w-20 border-r border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/50',\n }, [\n h('div', { class: 'p-2' }, [\n h(\n 'p',\n {\n class:\n 'text-[10px] text-gray-500 dark:text-gray-400 text-center mb-2 font-semibold',\n },\n 'TOOLBAR'\n ),\n h(\n 'div',\n { class: 'grid gap-1' },\n toolbarPage.grid.flatMap((row: (AACButton | null)[]) =>\n row.map((button: AACButton | null) => {\n if (!button) return null;\n\n const buttonMetric = buttonMetricsLookup.value[button.id];\n const effort = buttonMetric?.effort || 0;\n\n return h(\n 'button',\n {\n key: button.id,\n onClick: () => handleButtonClick(button),\n class:\n 'aspect-square p-1 rounded border border-gray-200 dark:border-gray-700 transition flex flex-col items-center justify-center gap-0.5 hover:bg-gray-200 dark:hover:bg-gray-700 relative',\n style: {\n backgroundColor: button.style?.backgroundColor || '#f3f4f6',\n borderColor: button.style?.borderColor || '#e5e7eb',\n color:\n button.style?.fontColor || getTextColor(button.style?.backgroundColor),\n },\n title: `${button.label}\\n${button.message || ''}`,\n },\n [\n buttonMetric && props.showEffortBadges && effort > 0\n ? h(\n 'div',\n {\n class:\n 'absolute top-0 right-0 px-0.5 py-0 text-[8px] font-semibold rounded bg-blue-600 text-white',\n },\n effort.toFixed(1)\n )\n : null,\n h(\n 'span',\n {\n class:\n 'text-[8px] sm:text-[9px] text-center font-medium leading-tight line-clamp-2',\n },\n button.label\n ),\n ]\n );\n })\n )\n ),\n ]),\n ]);\n };\n\n const renderGridCells = (): (VNode | null)[] => {\n if (!currentPage.value) return [];\n const rendered = new Set<string>();\n const cells: (VNode | null)[] = [];\n\n currentPage.value.grid.forEach((row: (AACButton | null)[], rowIndex: number) => {\n row.forEach((button: AACButton | null, colIndex: number) => {\n if (!button) {\n cells.push(\n h('div', {\n key: `empty-${rowIndex}-${colIndex}`,\n class: 'aspect-square',\n })\n );\n return;\n }\n\n if (rendered.has(button.id)) {\n cells.push(null);\n return;\n }\n rendered.add(button.id);\n\n const buttonMetric = buttonMetricsLookup.value[button.id];\n const effort = buttonMetric?.effort || 0;\n const targetPageId = button.targetPageId || button.semanticAction?.targetId;\n const hasLink = targetPageId && props.tree.pages[targetPageId];\n const colSpan = button.columnSpan || 1;\n const rowSpan = button.rowSpan || 1;\n const predictions =\n button.predictions || (button.parameters as { predictions?: string[] })?.predictions;\n const hasPredictions = predictions && predictions.length > 0;\n const isPredictionCell =\n button.contentType === 'AutoContent' &&\n (button.contentSubType || '').toLowerCase() === 'prediction';\n const isWorkspace = button.contentType === 'Workspace';\n\n const isHighlighted =\n props.highlight &&\n props.highlight.pageId === currentPageId.value &&\n (props.highlight.buttonId === button.id ||\n (props.highlight.x !== undefined && props.highlight.y !== undefined\n ? button.x === props.highlight.x && button.y === props.highlight.y\n : false) ||\n (props.highlight.label && button.label === props.highlight.label));\n\n const params = button.parameters as {\n image_id?: string;\n gridPageName?: string;\n imageData?: Buffer;\n };\n\n let imageSrc: string | null = null;\n let apiUrl: string | undefined = undefined;\n\n const resolvedEntry = button.resolvedImageEntry;\n const buttonImage = button.image;\n\n if (\n resolvedEntry &&\n typeof resolvedEntry === 'string' &&\n resolvedEntry.startsWith('data:image/')\n ) {\n imageSrc = resolvedEntry;\n } else if (\n buttonImage &&\n typeof buttonImage === 'string' &&\n buttonImage.startsWith('data:image/')\n ) {\n imageSrc = buttonImage;\n } else if (\n resolvedEntry &&\n typeof resolvedEntry === 'string' &&\n !resolvedEntry.startsWith('[') &&\n !resolvedEntry.startsWith('data:image/')\n ) {\n if (props.loadId) {\n const entryPath = resolvedEntry;\n const pathMatch = entryPath.match(/^(?:Grids\\/)?(.+)$/);\n if (pathMatch) {\n apiUrl = `/api/image/${props.loadId}/${encodeURIComponent(pathMatch[1])}`;\n }\n } else {\n imageSrc = resolvedEntry;\n }\n } else if (\n buttonImage &&\n typeof buttonImage === 'string' &&\n !buttonImage.startsWith('[')\n ) {\n imageSrc = buttonImage;\n }\n\n if (\n props.loadId &&\n !imageSrc &&\n !apiUrl &&\n params &&\n params.image_id &&\n typeof params.image_id === 'string'\n ) {\n apiUrl = `/api/image/${props.loadId}/${encodeURIComponent(params.image_id)}`;\n }\n\n if (isWorkspace) {\n cells.push(\n h(\n 'div',\n {\n key: button.id,\n class:\n 'relative p-3 rounded-lg border-2 bg-gray-50 text-gray-800 flex items-center gap-2',\n style: {\n borderColor: button.style?.borderColor || '#e5e7eb',\n gridColumn: `${colIndex + 1} / span ${colSpan}`,\n gridRow: `${rowIndex + 1} / span ${rowSpan}`,\n },\n },\n [\n h('div', { class: 'font-semibold text-sm' }, button.label || 'Workspace'),\n h('div', { class: 'text-xs text-gray-500 truncate' }, message.value || 'Chat writing area'),\n ]\n )\n );\n return;\n }\n\n cells.push(\n h(\n 'button',\n {\n key: button.id,\n onClick: () => handleButtonClick(button),\n ref: isHighlighted\n ? ((el: Element | null) => {\n highlightedButtonRef.value = el as HTMLButtonElement | null;\n }) as VNodeRef\n : undefined,\n class:\n `relative aspect-square p-2 rounded-lg border-2 transition flex flex-col items-center justify-center gap-1 hover:opacity-80 hover:scale-105 active:scale-95 ${\n isHighlighted ? 'ring-4 ring-amber-400 ring-offset-2 ring-offset-white' : ''\n }`,\n style: {\n backgroundColor: button.style?.backgroundColor || '#f3f4f6',\n borderColor: button.style?.borderColor || '#e5e7eb',\n color:\n button.style?.fontColor || getTextColor(button.style?.backgroundColor),\n gridColumn: `${colIndex + 1} / span ${colSpan}`,\n gridRow: `${rowIndex + 1} / span ${rowSpan}`,\n },\n },\n [\n buttonMetric && props.showEffortBadges\n ? h(\n 'div',\n {\n class:\n 'absolute top-1 right-1 px-1.5 py-0.5 text-xs font-semibold rounded bg-blue-600 text-white shadow-sm',\n },\n effort.toFixed(1)\n )\n : null,\n hasLink && props.showLinkIndicators\n ? h('div', {\n class:\n 'absolute top-1 left-1 w-2 h-2 bg-green-500 rounded-full shadow-sm',\n })\n : null,\n hasPredictions\n ? h(\n 'div',\n {\n onClick: (event: MouseEvent) => handleShowPredictions(button, event),\n class:\n 'absolute bottom-1 right-1 px-1.5 py-0.5 text-xs font-semibold rounded bg-purple-600 text-white shadow-sm cursor-pointer hover:bg-purple-700 transition',\n title: `Has ${predictions?.length} word form${\n predictions && predictions.length > 1 ? 's' : ''\n }`,\n },\n predictions?.length\n )\n : null,\n imageSrc || apiUrl\n ? h('img', {\n src: imageSrc || apiUrl,\n alt: button.label,\n class: 'max-h-12 object-contain',\n onError: (e: Event) => {\n const target = e.target as HTMLImageElement;\n console.warn('Image failed to load:', button.label, 'src:', target.src);\n target.style.display = 'none';\n },\n })\n : null,\n h('div', { class: 'flex flex-col items-center justify-center' }, [\n h(\n 'span',\n {\n class: 'text-xs sm:text-sm text-center font-medium leading-tight line-clamp-3',\n },\n button.label\n ),\n isPredictionCell && predictions && predictions.length > 0\n ? h(\n 'div',\n {\n class:\n 'mt-1 text-[10px] sm:text-xs text-center opacity-80 space-y-0.5',\n },\n [\n ...predictions.slice(0, 3).map((prediction: string, idx: number) =>\n h('div', { key: `${button.id}-pred-${idx}` }, prediction)\n ),\n predictions.length > 3 ? h('div', '…') : null,\n ]\n )\n : null,\n ]),\n button.message && button.message !== button.label && !isPredictionCell\n ? h(\n 'span',\n { class: 'text-[10px] sm:text-xs text-center opacity-75 line-clamp-2' },\n button.message\n )\n : null,\n ]\n )\n );\n });\n });\n\n return cells;\n };\n\n return () => {\n if (!currentPage.value) {\n return h(\n 'div',\n {\n class: `flex items-center justify-center h-96 bg-gray-100 dark:bg-gray-800 rounded-lg ${\n props.className\n }`,\n },\n [\n h('div', { class: 'text-center' }, [\n h('p', { class: 'text-gray-600 dark:text-gray-400' }, 'No pages available'),\n ]),\n ]\n );\n }\n\n const toolbarPage = props.tree.toolbarId ? props.tree.pages[props.tree.toolbarId] : null;\n const gridRows = currentPage.value.grid.length;\n const gridCols = gridRows > 0 ? currentPage.value.grid[0].length : 0;\n\n return h('div', {\n class: `bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden ${props.className}`,\n }, [\n props.showMessageBar\n ? h(\n 'div',\n {\n class:\n 'p-4 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/50',\n },\n [\n h('div', { class: 'flex items-start justify-between gap-4' }, [\n h('div', { class: 'flex-1 min-w-0' }, [\n message.value\n ? h('div', { class: 'space-y-2' }, [\n h(\n 'p',\n { class: 'text-lg text-gray-900 dark:text-white break-words' },\n message.value\n ),\n h('div', { class: 'flex gap-4 text-sm' }, [\n h(\n 'div',\n { class: 'text-gray-600 dark:text-gray-400' },\n `${currentWordCount.value} ${\n currentWordCount.value === 1 ? 'word' : 'words'\n }`\n ),\n props.buttonMetrics\n ? [\n h(\n 'div',\n { class: 'text-gray-600 dark:text-gray-400' },\n [\n 'Effort: ',\n h(\n 'span',\n { class: 'font-medium' },\n currentEffort.value.toFixed(2)\n ),\n ]\n ),\n h(\n 'div',\n { class: 'text-gray-600 dark:text-gray-400' },\n [\n 'Avg: ',\n h(\n 'span',\n { class: 'font-medium' },\n currentWordCount.value > 0\n ? (currentEffort.value / currentWordCount.value).toFixed(2)\n : '0.00'\n ),\n ]\n ),\n ]\n : null,\n ]),\n ])\n : h(\n 'p',\n { class: 'text-gray-500 dark:text-gray-400 italic' },\n 'Tap buttons to build a sentence...'\n ),\n ]),\n message.value\n ? h(\n 'button',\n {\n onClick: clearMessage,\n class:\n 'p-2 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-lg transition',\n 'aria-label': 'Clear message',\n },\n [\n h(\n 'svg',\n {\n class: 'h-5 w-5 text-gray-600 dark:text-gray-400',\n fill: 'none',\n stroke: 'currentColor',\n viewBox: '0 0 24 24',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'stroke-width': 2,\n d: 'M6 18L18 6M6 6l12 12',\n }),\n ]\n ),\n ]\n )\n : null,\n ]),\n ]\n )\n : null,\n h('div', { class: 'flex' }, [\n renderToolbar(toolbarPage),\n h('div', { class: 'flex-1' }, [\n h('div', {\n class: 'flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700',\n }, [\n h('div', { class: 'flex items-center gap-2' }, [\n pageHistory.value.length > 0\n ? h(\n 'button',\n {\n onClick: handleBack,\n class: 'p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition',\n 'aria-label': 'Go back',\n },\n [\n h(\n 'svg',\n {\n class: 'h-5 w-5 text-gray-600 dark:text-gray-400',\n fill: 'none',\n stroke: 'currentColor',\n viewBox: '0 0 24 24',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'stroke-width': 2,\n d: 'M15 19l-7-7 7-7',\n }),\n ]\n ),\n ]\n )\n : null,\n h(\n 'h3',\n { class: 'text-lg font-semibold text-gray-900 dark:text-white' },\n currentPage.value.name\n ),\n ]),\n h(\n 'div',\n { class: 'text-sm text-gray-500 dark:text-gray-400' },\n `${gridRows}×${gridCols} grid`\n ),\n ]),\n h(\n 'div',\n {\n class: 'p-4 gap-2 overflow-auto max-h-[600px]',\n style: {\n display: 'grid',\n gridTemplateColumns: `repeat(${gridCols}, minmax(0, 1fr))`,\n },\n },\n renderGridCells()\n ),\n Object.keys(props.tree.pages).length > 1\n ? h('div', { class: 'p-4 border-t border-gray-200 dark:border-gray-700' }, [\n h(\n 'p',\n { class: 'text-sm text-gray-600 dark:text-gray-400 mb-2' },\n `${Object.keys(props.tree.pages).length} pages in this vocabulary`\n ),\n ])\n : null,\n ]),\n ]),\n predictionsTooltip.value\n ? h(PredictionsTooltip, {\n predictions: predictionsTooltip.value.predictions,\n label: predictionsTooltip.value.label,\n position: predictionsTooltip.value.position,\n buttonMetricsLookup: predictionsTooltip.value.buttonMetricsLookup,\n onClose: predictionsTooltip.value.onClose,\n onWordClick: predictionsTooltip.value.onWordClick,\n })\n : null,\n ]);\n };\n },\n});\n\nexport default BoardViewer;\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAaP,IAAM,qBAAqB,gBAAgB;AAAA,EACzC,MAAM;AAAA,EACN,OAAO;AAAA,IACL,aAAa,EAAE,MAAM,OAA6B,UAAU,KAAK;AAAA,IACjE,OAAO,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IACtC,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,qBAAqB;AAAA,MACnB,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,SAAS,EAAE,MAAM,UAAkC,UAAU,KAAK;AAAA,IAClE,aAAa;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,MAAM,OAA0C;AAC9C,UAAM,qBAAqB,CAAC,MAAkB;AAC5C,UAAI,EAAE,kBAAkB,eAAe,CAAC,EAAE,OAAO,QAAQ,sBAAsB,GAAG;AAChF,cAAM,QAAQ;AAAA,MAChB;AAAA,IACF;AAEA,cAAU,MAAM;AACd,eAAS,iBAAiB,aAAa,kBAAkB;AAAA,IAC3D,CAAC;AAED,oBAAgB,MAAM;AACpB,eAAS,oBAAoB,aAAa,kBAAkB;AAAA,IAC9D,CAAC;AAED,UAAM,cAAc,MAAM;AACxB,YAAM,OAAO,OAAO,WAAW,cAAc,OAAO,aAAa,MAAM,MAAM,SAAS;AACtF,YAAM,OAAO,OAAO,WAAW,cAAc,OAAO,cAAc,MAAM,MAAM,SAAS;AACvF,aAAO;AAAA,QACL,MAAM,GAAG,KAAK,IAAI,MAAM,SAAS,GAAG,IAAI,CAAC;AAAA,QACzC,KAAK,GAAG,KAAK,IAAI,MAAM,SAAS,GAAG,IAAI,CAAC;AAAA,MAC1C;AAAA,IACF;AAEA,WAAO,MAAM;AACX,YAAM,gBAAgB,YAAY;AAElC,aAAO;AAAA,QACL;AAAA,QACA;AAAA,UACE,OACE;AAAA,UACF,OAAO;AAAA,UACP,SAAS,CAAC,MAAkB,EAAE,gBAAgB;AAAA,QAChD;AAAA,QACA;AAAA,UACE,EAAE,OAAO,EAAE,OAAO,yCAAyC,GAAG;AAAA,YAC5D;AAAA,cACE;AAAA,cACA,EAAE,OAAO,sDAAsD;AAAA,cAC/D,mBAAmB,MAAM,KAAK;AAAA,YAChC;AAAA,YACA;AAAA,cACE;AAAA,cACA;AAAA,gBACE,SAAS,MAAM;AAAA,gBACf,OAAO;AAAA,gBACP,cAAc;AAAA,cAChB;AAAA,cACA;AAAA,gBACE;AAAA,kBACE;AAAA,kBACA;AAAA,oBACE,OAAO;AAAA,oBACP,MAAM;AAAA,oBACN,QAAQ;AAAA,oBACR,SAAS;AAAA,kBACX;AAAA,kBACA;AAAA,oBACE,EAAE,QAAQ;AAAA,sBACR,kBAAkB;AAAA,sBAClB,mBAAmB;AAAA,sBACnB,gBAAgB;AAAA,sBAChB,GAAG;AAAA,oBACL,CAAC;AAAA,kBACH;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF,CAAC;AAAA,UACD;AAAA,YACE;AAAA,YACA,EAAE,OAAO,uBAAuB;AAAA,YAChC,MAAM,YAAY,IAAI,CAAC,MAAc,QAAgB;AACnD,oBAAM,gBAAgB,OAAO,OAAO,MAAM,mBAAmB,EAAE;AAAA,gBAC7D,CAAC,MAAoB,EAAE,UAAU;AAAA,cACnC;AACA,oBAAM,SAAS,eAAe;AAE9B,qBAAO;AAAA,gBACL;AAAA,gBACA;AAAA,kBACE,KAAK,GAAG,IAAI,IAAI,GAAG;AAAA,kBACnB,OACE;AAAA,kBACF,OAAO,iBAAiB,IAAI;AAAA,kBAC5B,SAAS,CAAC,MAAkB;AAC1B,sBAAE,gBAAgB;AAClB,wBAAI,MAAM,aAAa;AACrB,4BAAM,YAAY,MAAM,UAAU,CAAG;AACrC,4BAAM,QAAQ;AAAA,oBAChB;AAAA,kBACF;AAAA,gBACF;AAAA,gBACA;AAAA,kBACE,WAAW,SACP;AAAA,oBACE;AAAA,oBACA;AAAA,sBACE,OACE;AAAA,oBACJ;AAAA,oBACA,OAAO,QAAQ,CAAC;AAAA,kBAClB,IACA;AAAA,kBACJ;AAAA,gBACF;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,CAAC;AAEM,IAAM,cAAc,gBAAgB;AAAA,EACzC,MAAM;AAAA,EACN,OAAO;AAAA,IACL,MAAM,EAAE,MAAM,QAA8C,UAAU,KAAK;AAAA,IAC3E,eAAe,EAAE,MAAM,OAA0C,SAAS,KAAK;AAAA,IAC/E,gBAAgB,EAAE,MAAM,SAAS,SAAS,KAAK;AAAA,IAC/C,kBAAkB,EAAE,MAAM,SAAS,SAAS,KAAK;AAAA,IACjD,oBAAoB,EAAE,MAAM,SAAS,SAAS,KAAK;AAAA,IACnD,eAAe,EAAE,MAAM,QAAQ,SAAS,OAAU;AAAA,IAClD,kBAAkB,EAAE,MAAM,QAAQ,SAAS,OAAU;AAAA,IACrD,WAAW,EAAE,MAAM,QAAmD,SAAS,OAAU;AAAA,IACzF,eAAe;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA,cAAc;AAAA,MACZ,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA,WAAW,EAAE,MAAM,QAAQ,SAAS,GAAG;AAAA,IACvC,QAAQ,EAAE,MAAM,QAAQ,SAAS,OAAU;AAAA,EAC7C;AAAA,EACA,MAAM,OAAmC;AACvC,UAAM,uBAAuB,MAAM;AACjC,UAAI,MAAM,iBAAiB,MAAM,KAAK,MAAM,MAAM,aAAa,GAAG;AAChE,eAAO,MAAM;AAAA,MACf;AACA,UAAI,MAAM,KAAK,aAAa,MAAM,KAAK,UAAU,MAAM,KAAK,WAAW,MAAM,KAAK,WAAW;AAC3F,eAAO,MAAM,KAAK;AAAA,MACpB;AACA,UAAI,MAAM,KAAK,UAAU,MAAM,KAAK,MAAM,MAAM,KAAK,MAAM,GAAG;AAC5D,cAAM,WAAW,MAAM,KAAK,MAAM,MAAM,KAAK,MAAM;AACnD,cAAM,YACJ,SAAS,KAAK,YAAY,EAAE,SAAS,SAAS,KAC9C,SAAS,KAAK,YAAY,EAAE,SAAS,UAAU;AACjD,YAAI,CAAC,WAAW;AACd,iBAAO,MAAM,KAAK;AAAA,QACpB;AAAA,MACF;AACA,YAAM,YAAY,OAAO,OAAO,MAAM,KAAK,KAAK,EAAE;AAAA,QAChD,CAAC,MAAM,EAAE,KAAK,YAAY,MAAM;AAAA,MAClC;AACA,UAAI,WAAW;AACb,eAAO,UAAU;AAAA,MACnB;AACA,YAAM,iBAAiB,OAAO,OAAO,MAAM,KAAK,KAAK,EAAE;AAAA,QACrD,CAAC,MAAM,CAAC,EAAE,KAAK,YAAY,EAAE,SAAS,SAAS,KAAK,CAAC,EAAE,KAAK,YAAY,EAAE,SAAS,UAAU;AAAA,MAC/F;AACA,UAAI,gBAAgB;AAClB,eAAO,eAAe;AAAA,MACxB;AACA,YAAM,UAAU,OAAO,KAAK,MAAM,KAAK,KAAK;AAC5C,aAAO,QAAQ,SAAS,IAAI,QAAQ,CAAC,IAAI;AAAA,IAC3C;AAEA,UAAM,gBAAgB,IAAmB,qBAAqB,CAAC;AAC/D,UAAM,uBAAuB,IAA8B,IAAI;AAC/D,UAAM,cAAc,IAAe,CAAC,CAAC;AACrC,UAAM,UAAU,IAAI,EAAE;AACtB,UAAM,mBAAmB,IAAI,CAAC;AAC9B,UAAM,gBAAgB,IAAI,CAAC;AAE3B,UAAM,qBAAqB,IAAoC,IAAI;AAEnE,UAAM,sBAAsB,SAAS,MAAM;AACzC,UAAI,CAAC,MAAM,cAAe,QAAO,CAAC;AAClC,YAAM,SAA+C,CAAC;AACtD,YAAM,cAAc,QAAQ,CAAC,WAAyB;AACpD,eAAO,OAAO,EAAE,IAAI;AAAA,MACtB,CAAC;AACD,aAAO;AAAA,IACT,CAAC;AAED,UAAM,cAAc;AAAA,MAAS,MAC3B,cAAc,QAAQ,MAAM,KAAK,MAAM,cAAc,KAAK,IAAI;AAAA,IAChE;AAEA,UAAM,WAAW,CAAC,iBAA4C;AAC5D,UAAI,CAAC,gBAAgB,CAAC,MAAM,KAAK,MAAM,YAAY,EAAG,QAAO;AAC7D,UAAI,YAAY,OAAO;AACrB,oBAAY,QAAQ,CAAC,GAAG,YAAY,OAAO,YAAY,KAAK;AAAA,MAC9D;AACA,oBAAc,QAAQ;AACtB,UAAI,MAAM,cAAc;AACtB,cAAM,aAAa,YAAY;AAAA,MACjC;AACA,aAAO;AAAA,IACT;AAEA,UAAM,CAAC,MAAM,MAAM,MAAM,MAAM,MAAM,aAAa,GAAG,MAAM;AACzD,oBAAc,QAAQ,qBAAqB;AAC3C,kBAAY,QAAQ,CAAC;AAAA,IACvB,CAAC;AAED;AAAA,MACE,MAAM,MAAM;AAAA,MACZ,CAAC,WAA+B;AAC9B,YAAI,CAAC,OAAQ;AACb,YAAI,CAAC,MAAM,KAAK,MAAM,MAAM,EAAG;AAC/B,sBAAc,QAAQ;AACtB,oBAAY,QAAQ,CAAC;AACrB,YAAI,MAAM,cAAc;AACtB,gBAAM,aAAa,MAAM;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AAEA;AAAA,MACE;AAAA,QACE;AAAA,QACA,MAAM,MAAM,WAAW;AAAA,QACvB,MAAM,MAAM,WAAW;AAAA,QACvB,MAAM,MAAM,WAAW;AAAA,QACvB,MAAM,MAAM,WAAW;AAAA,MACzB;AAAA,MACA,MAAM;AACJ,iBAAS,MAAM;AACb,cAAI,qBAAqB,OAAO;AAC9B,iCAAqB,MAAM,eAAe;AAAA,cACxC,UAAU;AAAA,cACV,OAAO;AAAA,cACP,QAAQ;AAAA,YACV,CAAC;AAAA,UACH;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,cAAc,CAAC,MAAc,WAAmB;AACpD,uBAAiB,SAAS;AAC1B,oBAAc,SAAS;AAAA,IACzB;AAEA,UAAM,eAAe,MAAM;AACzB,cAAQ,QAAQ;AAChB,uBAAiB,QAAQ;AACzB,oBAAc,QAAQ;AAAA,IACxB;AAEA,UAAM,aAAa,MAAM;AACvB,UAAI,YAAY,MAAM,SAAS,GAAG;AAChC,cAAM,eAAe,YAAY,MAAM,YAAY,MAAM,SAAS,CAAC;AACnE,oBAAY,QAAQ,YAAY,MAAM,MAAM,GAAG,EAAE;AACjD,sBAAc,QAAQ,aAAa;AACnC,YAAI,MAAM,cAAc;AACtB,gBAAM,aAAa,aAAa,EAAE;AAAA,QACpC;AAAA,MACF;AAAA,IACF;AAEA,UAAM,oBAAoB,CAAC,WAAsB;AAC/C,UAAI,MAAM,eAAe;AACvB,cAAM,cAAc,MAAM;AAAA,MAC5B;AAEA,YAAM,SAAS,OAAO,gBAAgB,SAClC,OAAO,OAAO,eAAe,MAAM,IACnC;AACJ,YAAM,eAAe,OAAO,gBAAgB,OAAO,gBAAgB;AACnE,YAAM,SAAS,oBAAoB,MAAM,OAAO,EAAE,GAAG,UAAU;AAC/D,YAAM,YACJ,OAAO,gBAAgB,QAAQ,OAAO,WAAW,OAAO,SAAS;AAEnE,YAAM,iBAAiB,MAAM;AAC3B,gBAAQ,QAAQ,QAAQ,MACrB,KAAK,EACL,MAAM,KAAK,EACX,MAAM,GAAG,EAAE,EACX,KAAK,GAAG;AAAA,MACb;AAEA,YAAM,sBAAsB,MAAM;AAChC,gBAAQ,QAAQ,QAAQ,MAAM,MAAM,GAAG,EAAE;AAAA,MAC3C;AAEA,YAAM,aAAa,CAAC,SAAiB;AACnC,cAAM,UAAU,QAAQ,OAAO,SAAS;AACxC,gBAAQ,QAAQ,UACZ,GAAG,QAAQ,KAAK,GAAG,QAAQ,QAAQ,MAAM,EAAE,GAAG,OAAO,KACrD,QAAQ;AACZ,YAAI,SAAS;AACX,sBAAY,SAAS,MAAM;AAAA,QAC7B;AAAA,MACF;AAEA,UAAI,WAAW,iBAAiB,SAAS,YAAY,GAAG;AACtD;AAAA,MACF;AAEA,cAAQ,QAAQ;AAAA,QACd,KAAK;AACH,qBAAW;AACX;AAAA,QACF,KAAK;AACH,cAAI,MAAM,KAAK,UAAU,SAAS,MAAM,KAAK,MAAM,EAAG;AACtD;AAAA,QACF,KAAK;AACH,yBAAe;AACf;AAAA,QACF,KAAK;AACH,8BAAoB;AACpB;AAAA,QACF,KAAK;AACH,uBAAa;AACb;AAAA,QACF,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AACH,qBAAW,SAAS;AACpB;AAAA,QACF;AACE;AAAA,MACJ;AAEA,UAAI,gBAAgB,SAAS,YAAY,GAAG;AAC1C;AAAA,MACF;AAEA,iBAAW,SAAS;AAAA,IACtB;AAEA,UAAM,wBAAwB,CAAC,QAAmB,UAAsB;AACtE,YAAM,gBAAgB;AACtB,YAAM,cACJ,OAAO,eAAgB,OAAO,YAA2C;AAC3E,UAAI,eAAe,YAAY,SAAS,GAAG;AACzC,2BAAmB,QAAQ;AAAA,UACzB;AAAA,UACA,OAAO,OAAO;AAAA,UACd,UAAU,EAAE,GAAG,MAAM,SAAS,GAAG,MAAM,QAAQ;AAAA,UAC/C,qBAAqB,oBAAoB;AAAA,UACzC,aAAa,CAAC,MAAc,WAAmB;AAC7C,kBAAM,UAAU,QAAQ;AACxB,gBAAI,SAAS;AACX,sBAAQ,QAAQ,GAAG,QAAQ,KAAK,GAAG,QAAQ,QAAQ,MAAM,EAAE,GAAG,OAAO;AACrE,0BAAY,SAAS,MAAM;AAAA,YAC7B;AAAA,UACF;AAAA,UACA,SAAS,MAAM;AACb,+BAAmB,QAAQ;AAAA,UAC7B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,eAAe,CAAC,oBAA6B;AACjD,UAAI,CAAC,gBAAiB,QAAO;AAE7B,YAAM,MAAM,gBAAgB,QAAQ,KAAK,EAAE;AAC3C,UAAI,IAAI,WAAW,GAAG;AACpB,cAAM,IAAI,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE;AAC1C,cAAM,IAAI,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE;AAC1C,cAAM,IAAI,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE;AAC1C,cAAM,cAAc,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO;AACnD,eAAO,cAAc,MAAM,YAAY;AAAA,MACzC;AAEA,aAAO;AAAA,IACT;AAEA,UAAM,gBAAgB,CAAC,gBAA8C;AACnE,UAAI,CAAC,YAAa,QAAO;AAEzB,aAAO,EAAE,OAAO;AAAA,QACd,OACE;AAAA,MACJ,GAAG;AAAA,QACD,EAAE,OAAO,EAAE,OAAO,MAAM,GAAG;AAAA,UACzB;AAAA,YACE;AAAA,YACA;AAAA,cACE,OACE;AAAA,YACJ;AAAA,YACA;AAAA,UACF;AAAA,UACA;AAAA,YACE;AAAA,YACA,EAAE,OAAO,aAAa;AAAA,YACtB,YAAY,KAAK;AAAA,cAAQ,CAAC,QACxB,IAAI,IAAI,CAAC,WAA6B;AACpC,oBAAI,CAAC,OAAQ,QAAO;AAEpB,sBAAM,eAAe,oBAAoB,MAAM,OAAO,EAAE;AACxD,sBAAM,SAAS,cAAc,UAAU;AAEvC,uBAAO;AAAA,kBACL;AAAA,kBACA;AAAA,oBACE,KAAK,OAAO;AAAA,oBACZ,SAAS,MAAM,kBAAkB,MAAM;AAAA,oBACvC,OACE;AAAA,oBACF,OAAO;AAAA,sBACL,iBAAiB,OAAO,OAAO,mBAAmB;AAAA,sBAClD,aAAa,OAAO,OAAO,eAAe;AAAA,sBAC1C,OACE,OAAO,OAAO,aAAa,aAAa,OAAO,OAAO,eAAe;AAAA,oBACzE;AAAA,oBACA,OAAO,GAAG,OAAO,KAAK;AAAA,EAAK,OAAO,WAAW,EAAE;AAAA,kBACjD;AAAA,kBACA;AAAA,oBACE,gBAAgB,MAAM,oBAAoB,SAAS,IAC/C;AAAA,sBACE;AAAA,sBACA;AAAA,wBACE,OACE;AAAA,sBACJ;AAAA,sBACA,OAAO,QAAQ,CAAC;AAAA,oBAClB,IACA;AAAA,oBACJ;AAAA,sBACE;AAAA,sBACA;AAAA,wBACE,OACE;AAAA,sBACJ;AAAA,sBACA,OAAO;AAAA,oBACT;AAAA,kBACF;AAAA,gBACF;AAAA,cACF,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAEA,UAAM,kBAAkB,MAAwB;AAC9C,UAAI,CAAC,YAAY,MAAO,QAAO,CAAC;AAChC,YAAM,WAAW,oBAAI,IAAY;AACjC,YAAM,QAA0B,CAAC;AAEjC,kBAAY,MAAM,KAAK,QAAQ,CAAC,KAA2B,aAAqB;AAC9E,YAAI,QAAQ,CAAC,QAA0B,aAAqB;AAC1D,cAAI,CAAC,QAAQ;AACX,kBAAM;AAAA,cACJ,EAAE,OAAO;AAAA,gBACP,KAAK,SAAS,QAAQ,IAAI,QAAQ;AAAA,gBAClC,OAAO;AAAA,cACT,CAAC;AAAA,YACH;AACA;AAAA,UACF;AAEA,cAAI,SAAS,IAAI,OAAO,EAAE,GAAG;AAC3B,kBAAM,KAAK,IAAI;AACf;AAAA,UACF;AACA,mBAAS,IAAI,OAAO,EAAE;AAEtB,gBAAM,eAAe,oBAAoB,MAAM,OAAO,EAAE;AACxD,gBAAM,SAAS,cAAc,UAAU;AACvC,gBAAM,eAAe,OAAO,gBAAgB,OAAO,gBAAgB;AACnE,gBAAM,UAAU,gBAAgB,MAAM,KAAK,MAAM,YAAY;AAC7D,gBAAM,UAAU,OAAO,cAAc;AACrC,gBAAM,UAAU,OAAO,WAAW;AAClC,gBAAM,cACJ,OAAO,eAAgB,OAAO,YAA2C;AAC3E,gBAAM,iBAAiB,eAAe,YAAY,SAAS;AAC3D,gBAAM,mBACJ,OAAO,gBAAgB,kBACtB,OAAO,kBAAkB,IAAI,YAAY,MAAM;AAClD,gBAAM,cAAc,OAAO,gBAAgB;AAE3C,gBAAM,gBACJ,MAAM,aACN,MAAM,UAAU,WAAW,cAAc,UACxC,MAAM,UAAU,aAAa,OAAO,OAClC,MAAM,UAAU,MAAM,UAAa,MAAM,UAAU,MAAM,SACtD,OAAO,MAAM,MAAM,UAAU,KAAK,OAAO,MAAM,MAAM,UAAU,IAC/D,UACH,MAAM,UAAU,SAAS,OAAO,UAAU,MAAM,UAAU;AAE/D,gBAAM,SAAS,OAAO;AAMtB,cAAI,WAA0B;AAC9B,cAAI,SAA6B;AAEjC,gBAAM,gBAAgB,OAAO;AAC7B,gBAAM,cAAc,OAAO;AAE3B,cACE,iBACA,OAAO,kBAAkB,YACzB,cAAc,WAAW,aAAa,GACtC;AACA,uBAAW;AAAA,UACb,WACE,eACA,OAAO,gBAAgB,YACvB,YAAY,WAAW,aAAa,GACpC;AACA,uBAAW;AAAA,UACb,WACE,iBACA,OAAO,kBAAkB,YACzB,CAAC,cAAc,WAAW,GAAG,KAC7B,CAAC,cAAc,WAAW,aAAa,GACvC;AACA,gBAAI,MAAM,QAAQ;AAChB,oBAAM,YAAY;AAClB,oBAAM,YAAY,UAAU,MAAM,oBAAoB;AACtD,kBAAI,WAAW;AACb,yBAAS,cAAc,MAAM,MAAM,IAAI,mBAAmB,UAAU,CAAC,CAAC,CAAC;AAAA,cACzE;AAAA,YACF,OAAO;AACL,yBAAW;AAAA,YACb;AAAA,UACF,WACE,eACA,OAAO,gBAAgB,YACvB,CAAC,YAAY,WAAW,GAAG,GAC3B;AACA,uBAAW;AAAA,UACb;AAEA,cACE,MAAM,UACN,CAAC,YACD,CAAC,UACD,UACA,OAAO,YACP,OAAO,OAAO,aAAa,UAC3B;AACA,qBAAS,cAAc,MAAM,MAAM,IAAI,mBAAmB,OAAO,QAAQ,CAAC;AAAA,UAC5E;AAEA,cAAI,aAAa;AACf,kBAAM;AAAA,cACJ;AAAA,gBACE;AAAA,gBACA;AAAA,kBACE,KAAK,OAAO;AAAA,kBACZ,OACE;AAAA,kBACF,OAAO;AAAA,oBACL,aAAa,OAAO,OAAO,eAAe;AAAA,oBAC1C,YAAY,GAAG,WAAW,CAAC,WAAW,OAAO;AAAA,oBAC7C,SAAS,GAAG,WAAW,CAAC,WAAW,OAAO;AAAA,kBAC5C;AAAA,gBACF;AAAA,gBACA;AAAA,kBACE,EAAE,OAAO,EAAE,OAAO,wBAAwB,GAAG,OAAO,SAAS,WAAW;AAAA,kBACxE,EAAE,OAAO,EAAE,OAAO,iCAAiC,GAAG,QAAQ,SAAS,mBAAmB;AAAA,gBAC5F;AAAA,cACF;AAAA,YACF;AACA;AAAA,UACF;AAEA,gBAAM;AAAA,YACJ;AAAA,cACE;AAAA,cACA;AAAA,gBACE,KAAK,OAAO;AAAA,gBACZ,SAAS,MAAM,kBAAkB,MAAM;AAAA,gBACvC,KAAK,iBACA,CAAC,OAAuB;AACvB,uCAAqB,QAAQ;AAAA,gBAC/B,KACA;AAAA,gBACJ,OACE,8JACE,gBAAgB,0DAA0D,EAC5E;AAAA,gBACF,OAAO;AAAA,kBACL,iBAAiB,OAAO,OAAO,mBAAmB;AAAA,kBAClD,aAAa,OAAO,OAAO,eAAe;AAAA,kBAC1C,OACE,OAAO,OAAO,aAAa,aAAa,OAAO,OAAO,eAAe;AAAA,kBACvE,YAAY,GAAG,WAAW,CAAC,WAAW,OAAO;AAAA,kBAC7C,SAAS,GAAG,WAAW,CAAC,WAAW,OAAO;AAAA,gBAC5C;AAAA,cACF;AAAA,cACA;AAAA,gBACE,gBAAgB,MAAM,mBAClB;AAAA,kBACE;AAAA,kBACA;AAAA,oBACE,OACE;AAAA,kBACJ;AAAA,kBACA,OAAO,QAAQ,CAAC;AAAA,gBAClB,IACA;AAAA,gBACJ,WAAW,MAAM,qBACb,EAAE,OAAO;AAAA,kBACP,OACE;AAAA,gBACJ,CAAC,IACD;AAAA,gBACJ,iBACI;AAAA,kBACE;AAAA,kBACA;AAAA,oBACE,SAAS,CAAC,UAAsB,sBAAsB,QAAQ,KAAK;AAAA,oBACnE,OACE;AAAA,oBACF,OAAO,OAAO,aAAa,MAAM,aAC/B,eAAe,YAAY,SAAS,IAAI,MAAM,EAChD;AAAA,kBACF;AAAA,kBACA,aAAa;AAAA,gBACf,IACA;AAAA,gBACJ,YAAY,SACR,EAAE,OAAO;AAAA,kBACP,KAAK,YAAY;AAAA,kBACjB,KAAK,OAAO;AAAA,kBACZ,OAAO;AAAA,kBACP,SAAS,CAAC,MAAa;AACrB,0BAAM,SAAS,EAAE;AACjB,4BAAQ,KAAK,yBAAyB,OAAO,OAAO,QAAQ,OAAO,GAAG;AACtE,2BAAO,MAAM,UAAU;AAAA,kBACzB;AAAA,gBACF,CAAC,IACD;AAAA,gBACJ,EAAE,OAAO,EAAE,OAAO,4CAA4C,GAAG;AAAA,kBAC/D;AAAA,oBACE;AAAA,oBACA;AAAA,sBACE,OAAO;AAAA,oBACT;AAAA,oBACA,OAAO;AAAA,kBACT;AAAA,kBACA,oBAAoB,eAAe,YAAY,SAAS,IACpD;AAAA,oBACE;AAAA,oBACA;AAAA,sBACE,OACE;AAAA,oBACJ;AAAA,oBACA;AAAA,sBACE,GAAG,YAAY,MAAM,GAAG,CAAC,EAAE;AAAA,wBAAI,CAAC,YAAoB,QAClD,EAAE,OAAO,EAAE,KAAK,GAAG,OAAO,EAAE,SAAS,GAAG,GAAG,GAAG,UAAU;AAAA,sBAC1D;AAAA,sBACA,YAAY,SAAS,IAAI,EAAE,OAAO,QAAG,IAAI;AAAA,oBAC3C;AAAA,kBACF,IACA;AAAA,gBACN,CAAC;AAAA,gBACD,OAAO,WAAW,OAAO,YAAY,OAAO,SAAS,CAAC,mBAClD;AAAA,kBACE;AAAA,kBACA,EAAE,OAAO,6DAA6D;AAAA,kBACtE,OAAO;AAAA,gBACT,IACA;AAAA,cACN;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAED,aAAO;AAAA,IACT;AAEA,WAAO,MAAM;AACX,UAAI,CAAC,YAAY,OAAO;AACtB,eAAO;AAAA,UACL;AAAA,UACA;AAAA,YACE,OAAO,iFACL,MAAM,SACR;AAAA,UACF;AAAA,UACA;AAAA,YACE,EAAE,OAAO,EAAE,OAAO,cAAc,GAAG;AAAA,cACjC,EAAE,KAAK,EAAE,OAAO,mCAAmC,GAAG,oBAAoB;AAAA,YAC5E,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAEA,YAAM,cAAc,MAAM,KAAK,YAAY,MAAM,KAAK,MAAM,MAAM,KAAK,SAAS,IAAI;AACpF,YAAM,WAAW,YAAY,MAAM,KAAK;AACxC,YAAM,WAAW,WAAW,IAAI,YAAY,MAAM,KAAK,CAAC,EAAE,SAAS;AAEnE,aAAO,EAAE,OAAO;AAAA,QACd,OAAO,kEAAkE,MAAM,SAAS;AAAA,MAC1F,GAAG;AAAA,QACD,MAAM,iBACF;AAAA,UACE;AAAA,UACA;AAAA,YACE,OACE;AAAA,UACJ;AAAA,UACA;AAAA,YACE,EAAE,OAAO,EAAE,OAAO,yCAAyC,GAAG;AAAA,cAC5D,EAAE,OAAO,EAAE,OAAO,iBAAiB,GAAG;AAAA,gBACpC,QAAQ,QACJ,EAAE,OAAO,EAAE,OAAO,YAAY,GAAG;AAAA,kBAC/B;AAAA,oBACE;AAAA,oBACA,EAAE,OAAO,oDAAoD;AAAA,oBAC7D,QAAQ;AAAA,kBACV;AAAA,kBACA,EAAE,OAAO,EAAE,OAAO,qBAAqB,GAAG;AAAA,oBACxC;AAAA,sBACE;AAAA,sBACA,EAAE,OAAO,mCAAmC;AAAA,sBAC5C,GAAG,iBAAiB,KAAK,IACvB,iBAAiB,UAAU,IAAI,SAAS,OAC1C;AAAA,oBACF;AAAA,oBACA,MAAM,gBACF;AAAA,sBACE;AAAA,wBACE;AAAA,wBACA,EAAE,OAAO,mCAAmC;AAAA,wBAC5C;AAAA,0BACE;AAAA,0BACA;AAAA,4BACE;AAAA,4BACA,EAAE,OAAO,cAAc;AAAA,4BACvB,cAAc,MAAM,QAAQ,CAAC;AAAA,0BAC/B;AAAA,wBACF;AAAA,sBACF;AAAA,sBACA;AAAA,wBACE;AAAA,wBACA,EAAE,OAAO,mCAAmC;AAAA,wBAC5C;AAAA,0BACE;AAAA,0BACA;AAAA,4BACE;AAAA,4BACA,EAAE,OAAO,cAAc;AAAA,4BACvB,iBAAiB,QAAQ,KACpB,cAAc,QAAQ,iBAAiB,OAAO,QAAQ,CAAC,IACxD;AAAA,0BACN;AAAA,wBACF;AAAA,sBACF;AAAA,oBACF,IACA;AAAA,kBACN,CAAC;AAAA,gBACH,CAAC,IACD;AAAA,kBACE;AAAA,kBACA,EAAE,OAAO,0CAA0C;AAAA,kBACnD;AAAA,gBACF;AAAA,cACN,CAAC;AAAA,cACD,QAAQ,QACJ;AAAA,gBACE;AAAA,gBACA;AAAA,kBACE,SAAS;AAAA,kBACT,OACE;AAAA,kBACF,cAAc;AAAA,gBAChB;AAAA,gBACA;AAAA,kBACE;AAAA,oBACE;AAAA,oBACA;AAAA,sBACE,OAAO;AAAA,sBACP,MAAM;AAAA,sBACN,QAAQ;AAAA,sBACR,SAAS;AAAA,oBACX;AAAA,oBACA;AAAA,sBACE,EAAE,QAAQ;AAAA,wBACR,kBAAkB;AAAA,wBAClB,mBAAmB;AAAA,wBACnB,gBAAgB;AAAA,wBAChB,GAAG;AAAA,sBACL,CAAC;AAAA,oBACH;AAAA,kBACF;AAAA,gBACF;AAAA,cACF,IACA;AAAA,YACN,CAAC;AAAA,UACH;AAAA,QACF,IACA;AAAA,QACJ,EAAE,OAAO,EAAE,OAAO,OAAO,GAAG;AAAA,UAC1B,cAAc,WAAW;AAAA,UACzB,EAAE,OAAO,EAAE,OAAO,SAAS,GAAG;AAAA,YAC5B,EAAE,OAAO;AAAA,cACP,OAAO;AAAA,YACT,GAAG;AAAA,cACD,EAAE,OAAO,EAAE,OAAO,0BAA0B,GAAG;AAAA,gBAC7C,YAAY,MAAM,SAAS,IACvB;AAAA,kBACE;AAAA,kBACA;AAAA,oBACE,SAAS;AAAA,oBACT,OAAO;AAAA,oBACP,cAAc;AAAA,kBAChB;AAAA,kBACA;AAAA,oBACE;AAAA,sBACE;AAAA,sBACA;AAAA,wBACE,OAAO;AAAA,wBACP,MAAM;AAAA,wBACN,QAAQ;AAAA,wBACR,SAAS;AAAA,sBACX;AAAA,sBACA;AAAA,wBACE,EAAE,QAAQ;AAAA,0BACR,kBAAkB;AAAA,0BAClB,mBAAmB;AAAA,0BACnB,gBAAgB;AAAA,0BAChB,GAAG;AAAA,wBACL,CAAC;AAAA,sBACH;AAAA,oBACF;AAAA,kBACF;AAAA,gBACF,IACA;AAAA,gBACJ;AAAA,kBACE;AAAA,kBACA,EAAE,OAAO,sDAAsD;AAAA,kBAC/D,YAAY,MAAM;AAAA,gBACpB;AAAA,cACF,CAAC;AAAA,cACD;AAAA,gBACE;AAAA,gBACA,EAAE,OAAO,2CAA2C;AAAA,gBACpD,GAAG,QAAQ,OAAI,QAAQ;AAAA,cACzB;AAAA,YACF,CAAC;AAAA,YACD;AAAA,cACE;AAAA,cACA;AAAA,gBACE,OAAO;AAAA,gBACP,OAAO;AAAA,kBACL,SAAS;AAAA,kBACT,qBAAqB,UAAU,QAAQ;AAAA,gBACzC;AAAA,cACF;AAAA,cACA,gBAAgB;AAAA,YAClB;AAAA,YACA,OAAO,KAAK,MAAM,KAAK,KAAK,EAAE,SAAS,IACnC,EAAE,OAAO,EAAE,OAAO,oDAAoD,GAAG;AAAA,cACvE;AAAA,gBACE;AAAA,gBACA,EAAE,OAAO,gDAAgD;AAAA,gBACzD,GAAG,OAAO,KAAK,MAAM,KAAK,KAAK,EAAE,MAAM;AAAA,cACzC;AAAA,YACF,CAAC,IACD;AAAA,UACN,CAAC;AAAA,QACH,CAAC;AAAA,QACD,mBAAmB,QACf,EAAE,oBAAoB;AAAA,UACpB,aAAa,mBAAmB,MAAM;AAAA,UACtC,OAAO,mBAAmB,MAAM;AAAA,UAChC,UAAU,mBAAmB,MAAM;AAAA,UACnC,qBAAqB,mBAAmB,MAAM;AAAA,UAC9C,SAAS,mBAAmB,MAAM;AAAA,UAClC,aAAa,mBAAmB,MAAM;AAAA,QACxC,CAAC,IACD;AAAA,MACN,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../src/vue/BoardViewer.ts"],"sourcesContent":["import {\n defineComponent,\n ref,\n computed,\n watch,\n onMounted,\n onBeforeUnmount,\n nextTick,\n h,\n type PropType,\n type VNode,\n type VNodeRef,\n} from 'vue';\nimport type { AACPage, AACButton } from '@willwade/aac-processors';\nimport type { BoardViewerProps, ButtonMetric } from '../types';\n\ninterface PredictionsTooltipProps {\n predictions: string[];\n label: string;\n position: { x: number; y: number };\n buttonMetricsLookup: { [buttonId: string]: ButtonMetric };\n onClose: () => void;\n onWordClick?: (word: string, effort: number) => void;\n pos?: string;\n}\n\nconst PredictionsTooltip = defineComponent({\n name: 'PredictionsTooltip',\n props: {\n predictions: { type: Array as PropType<string[]>, required: true },\n label: { type: String, required: true },\n position: {\n type: Object as PropType<{ x: number; y: number }>,\n required: true,\n },\n buttonMetricsLookup: {\n type: Object as PropType<{ [buttonId: string]: ButtonMetric }>,\n required: true,\n },\n onClose: { type: Function as PropType<() => void>, required: true },\n onWordClick: {\n type: Function as PropType<(word: string, effort: number) => void>,\n required: false,\n },\n pos: { type: String, default: undefined },\n },\n setup(props: Readonly<PredictionsTooltipProps>) {\n const handleClickOutside = (e: MouseEvent) => {\n if (e.target instanceof HTMLElement && !e.target.closest('.predictions-tooltip')) {\n props.onClose();\n }\n };\n\n onMounted(() => {\n document.addEventListener('mousedown', handleClickOutside);\n });\n\n onBeforeUnmount(() => {\n document.removeEventListener('mousedown', handleClickOutside);\n });\n\n const getPosition = () => {\n const maxX = typeof window !== 'undefined' ? window.innerWidth - 200 : props.position.x;\n const maxY = typeof window !== 'undefined' ? window.innerHeight - 150 : props.position.y;\n return {\n left: `${Math.min(props.position.x, maxX)}px`,\n top: `${Math.min(props.position.y, maxY)}px`,\n };\n };\n\n return () => {\n const positionStyle = getPosition();\n\n return h(\n 'div',\n {\n class:\n 'predictions-tooltip fixed z-50 bg-white dark:bg-gray-800 rounded-lg shadow-xl border-2 border-purple-500 p-3 max-w-xs',\n style: positionStyle,\n onClick: (e: MouseEvent) => e.stopPropagation(),\n },\n [\n h('div', { class: 'flex items-center justify-between mb-2' }, [\n h(\n 'h4',\n { class: 'text-sm font-semibold text-gray-900 dark:text-white flex items-center gap-1.5' },\n [\n `Word forms for \"${props.label}\"`,\n props.pos && props.pos !== 'Unknown' && props.pos !== 'Ignore'\n ? h(\n 'span',\n {\n class: `px-1.5 py-0 text-[10px] font-semibold rounded text-white ${\n props.pos === 'Verb' ? 'bg-orange-500' :\n props.pos === 'Noun' ? 'bg-teal-500' :\n props.pos === 'Pronoun' ? 'bg-pink-500' :\n props.pos === 'Adjective' ? 'bg-yellow-500' :\n 'bg-gray-500'\n }`,\n },\n props.pos\n )\n : null,\n ]\n ),\n h(\n 'button',\n {\n onClick: props.onClose,\n class: 'p-1 hover:bg-gray-100 dark:hover:bg-gray-700 rounded',\n 'aria-label': 'Close',\n },\n [\n h(\n 'svg',\n {\n class: 'h-4 w-4 text-gray-600 dark:text-gray-400',\n fill: 'none',\n stroke: 'currentColor',\n viewBox: '0 0 24 24',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'stroke-width': 2,\n d: 'M6 18L18 6M6 6l12 12',\n }),\n ]\n ),\n ]\n ),\n ]),\n h(\n 'div',\n { class: 'flex flex-wrap gap-1' },\n props.predictions.map((word: string, idx: number) => {\n const metricForWord = Object.values(props.buttonMetricsLookup).find(\n (m: ButtonMetric) => m.label === word\n );\n const effort = metricForWord?.effort;\n\n return h(\n 'span',\n {\n key: `${word}-${idx}`,\n class:\n 'px-2 py-1 bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 rounded text-xs font-medium relative cursor-pointer hover:bg-purple-200 dark:hover:bg-purple-800 transition',\n title: `Click to add \"${word}\" to message`,\n onClick: (e: MouseEvent) => {\n e.stopPropagation();\n if (props.onWordClick) {\n props.onWordClick(word, effort || 1.0);\n props.onClose();\n }\n },\n },\n [\n effort !== undefined\n ? h(\n 'span',\n {\n class:\n 'absolute -top-1 -right-1 px-1 py-0 text-[8px] font-semibold rounded bg-blue-600 text-white shadow-xs',\n },\n effort.toFixed(1)\n )\n : null,\n word,\n ]\n );\n })\n ),\n ]\n );\n };\n },\n});\n\nexport const BoardViewer = defineComponent({\n name: 'BoardViewer',\n props: {\n tree: { type: Object as PropType<BoardViewerProps['tree']>, required: true },\n buttonMetrics: { type: Array as PropType<ButtonMetric[] | null>, default: null },\n showMessageBar: { type: Boolean, default: true },\n showEffortBadges: { type: Boolean, default: true },\n showLinkIndicators: { type: Boolean, default: true },\n initialPageId: { type: String, default: undefined },\n navigateToPageId: { type: String, default: undefined },\n highlight: { type: Object as PropType<BoardViewerProps['highlight']>, default: undefined },\n onButtonClick: {\n type: Function as PropType<(button: AACButton) => void>,\n default: undefined,\n },\n onPageChange: {\n type: Function as PropType<(pageId: string) => void>,\n default: undefined,\n },\n className: { type: String, default: '' },\n loadId: { type: String, default: undefined },\n },\n setup(props: Readonly<BoardViewerProps>) {\n const resolveInitialPageId = () => {\n if (props.initialPageId && props.tree.pages[props.initialPageId]) {\n return props.initialPageId;\n }\n if (props.tree.toolbarId && props.tree.rootId && props.tree.rootId !== props.tree.toolbarId) {\n return props.tree.rootId;\n }\n if (props.tree.rootId && props.tree.pages[props.tree.rootId]) {\n const rootPage = props.tree.pages[props.tree.rootId];\n const isToolbar =\n rootPage.name.toLowerCase().includes('toolbar') ||\n rootPage.name.toLowerCase().includes('tool bar');\n if (!isToolbar) {\n return props.tree.rootId;\n }\n }\n const startPage = Object.values(props.tree.pages).find(\n (p) => p.name.toLowerCase() === 'start'\n );\n if (startPage) {\n return startPage.id;\n }\n const nonToolbarPage = Object.values(props.tree.pages).find(\n (p) => !p.name.toLowerCase().includes('toolbar') && !p.name.toLowerCase().includes('tool bar')\n );\n if (nonToolbarPage) {\n return nonToolbarPage.id;\n }\n const pageIds = Object.keys(props.tree.pages);\n return pageIds.length > 0 ? pageIds[0] : null;\n };\n\n const currentPageId = ref<string | null>(resolveInitialPageId());\n const highlightedButtonRef = ref<HTMLButtonElement | null>(null);\n const pageHistory = ref<AACPage[]>([]);\n const message = ref('');\n const currentWordCount = ref(0);\n const currentEffort = ref(0);\n\n const predictionsTooltip = ref<PredictionsTooltipProps | null>(null);\n\n const buttonMetricsLookup = computed(() => {\n if (!props.buttonMetrics) return {};\n const lookup: { [buttonId: string]: ButtonMetric } = {};\n props.buttonMetrics.forEach((metric: ButtonMetric) => {\n lookup[metric.id] = metric;\n });\n return lookup;\n });\n\n const currentPage = computed(() =>\n currentPageId.value ? props.tree.pages[currentPageId.value] : null\n );\n\n const goToPage = (targetPageId: string | undefined | null) => {\n if (!targetPageId || !props.tree.pages[targetPageId]) return false;\n if (currentPage.value) {\n pageHistory.value = [...pageHistory.value, currentPage.value];\n }\n currentPageId.value = targetPageId;\n if (props.onPageChange) {\n props.onPageChange(targetPageId);\n }\n return true;\n };\n\n watch([() => props.tree, () => props.initialPageId], () => {\n currentPageId.value = resolveInitialPageId();\n pageHistory.value = [];\n });\n\n watch(\n () => props.navigateToPageId,\n (pageId: string | undefined) => {\n if (!pageId) return;\n if (!props.tree.pages[pageId]) return;\n currentPageId.value = pageId;\n pageHistory.value = [];\n if (props.onPageChange) {\n props.onPageChange(pageId);\n }\n }\n );\n\n watch(\n [\n currentPageId,\n () => props.highlight?.pageId,\n () => props.highlight?.x,\n () => props.highlight?.y,\n () => props.highlight?.label,\n ],\n () => {\n nextTick(() => {\n if (highlightedButtonRef.value) {\n highlightedButtonRef.value.scrollIntoView({\n behavior: 'smooth',\n block: 'center',\n inline: 'center',\n });\n }\n });\n }\n );\n\n const updateStats = (word: string, effort: number) => {\n currentWordCount.value += 1;\n currentEffort.value += effort;\n };\n\n const clearMessage = () => {\n message.value = '';\n currentWordCount.value = 0;\n currentEffort.value = 0;\n };\n\n const handleBack = () => {\n if (pageHistory.value.length > 0) {\n const previousPage = pageHistory.value[pageHistory.value.length - 1];\n pageHistory.value = pageHistory.value.slice(0, -1);\n currentPageId.value = previousPage.id;\n if (props.onPageChange) {\n props.onPageChange(previousPage.id);\n }\n }\n };\n\n const handleButtonClick = (button: AACButton) => {\n if (props.onButtonClick) {\n props.onButtonClick(button);\n }\n\n const intent = button.semanticAction?.intent\n ? String(button.semanticAction.intent)\n : undefined;\n const targetPageId = button.targetPageId || button.semanticAction?.targetId;\n const effort = buttonMetricsLookup.value[button.id]?.effort || 1;\n const textValue =\n button.semanticAction?.text || button.message || button.label || '';\n\n const deleteLastWord = () => {\n message.value = message.value\n .trim()\n .split(/\\s+/)\n .slice(0, -1)\n .join(' ');\n };\n\n const deleteLastCharacter = () => {\n message.value = message.value.slice(0, -1);\n };\n\n const appendText = (word: string) => {\n const trimmed = word || button.label || '';\n message.value = trimmed\n ? `${message.value}${message.value ? ' ' : ''}${trimmed}`\n : message.value;\n if (trimmed) {\n updateStats(trimmed, effort);\n }\n };\n\n if (intent === 'NAVIGATE_TO' && goToPage(targetPageId)) {\n return;\n }\n\n switch (intent) {\n case 'GO_BACK':\n handleBack();\n return;\n case 'GO_HOME':\n if (props.tree.rootId && goToPage(props.tree.rootId)) return;\n break;\n case 'DELETE_WORD':\n deleteLastWord();\n return;\n case 'DELETE_CHARACTER':\n deleteLastCharacter();\n return;\n case 'CLEAR_TEXT':\n clearMessage();\n return;\n case 'SPEAK_IMMEDIATE':\n case 'SPEAK_TEXT':\n case 'INSERT_TEXT':\n appendText(textValue);\n return;\n default:\n break;\n }\n\n if (targetPageId && goToPage(targetPageId)) {\n return;\n }\n\n appendText(textValue);\n };\n\n const handleShowPredictions = (button: AACButton, event: MouseEvent) => {\n event.stopPropagation();\n const predictions =\n button.predictions || (button.parameters as { predictions?: string[] })?.predictions;\n const buttonMetric = buttonMetricsLookup.value[button.id];\n if (predictions && predictions.length > 0) {\n predictionsTooltip.value = {\n predictions,\n label: button.label,\n position: { x: event.clientX, y: event.clientY },\n buttonMetricsLookup: buttonMetricsLookup.value,\n pos: buttonMetric?.pos || button.pos,\n onWordClick: (word: string, effort: number) => {\n const trimmed = word || '';\n if (trimmed) {\n message.value = `${message.value}${message.value ? ' ' : ''}${trimmed}`;\n updateStats(trimmed, effort);\n }\n },\n onClose: () => {\n predictionsTooltip.value = null;\n },\n };\n }\n };\n\n const getTextColor = (backgroundColor?: string) => {\n if (!backgroundColor) return '#111827';\n\n const hex = backgroundColor.replace('#', '');\n if (hex.length === 6) {\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\n return brightness >= 128 ? '#111827' : '#f9fafb';\n }\n\n return '#111827';\n };\n\n const renderToolbar = (toolbarPage: AACPage | null): VNode | null => {\n if (!toolbarPage) return null;\n\n return h('div', {\n class:\n 'w-16 sm:w-20 border-r border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/50',\n }, [\n h('div', { class: 'p-2' }, [\n h(\n 'p',\n {\n class:\n 'text-[10px] text-gray-500 dark:text-gray-400 text-center mb-2 font-semibold',\n },\n 'TOOLBAR'\n ),\n h(\n 'div',\n { class: 'grid gap-1' },\n toolbarPage.grid.flatMap((row: (AACButton | null)[]) =>\n row.map((button: AACButton | null) => {\n if (!button) return null;\n\n const buttonMetric = buttonMetricsLookup.value[button.id];\n const effort = buttonMetric?.effort || 0;\n\n return h(\n 'button',\n {\n key: button.id,\n onClick: () => handleButtonClick(button),\n class:\n 'aspect-square p-1 rounded border border-gray-200 dark:border-gray-700 transition flex flex-col items-center justify-center gap-0.5 hover:bg-gray-200 dark:hover:bg-gray-700 relative',\n style: {\n backgroundColor: button.style?.backgroundColor || '#f3f4f6',\n borderColor: button.style?.borderColor || '#e5e7eb',\n color:\n button.style?.fontColor || getTextColor(button.style?.backgroundColor),\n },\n title: `${button.label}\\n${button.message || ''}`,\n },\n [\n buttonMetric && props.showEffortBadges && effort > 0\n ? h(\n 'div',\n {\n class:\n 'absolute top-0 right-0 px-0.5 py-0 text-[8px] font-semibold rounded bg-blue-600 text-white',\n },\n effort.toFixed(1)\n )\n : null,\n h(\n 'span',\n {\n class:\n 'text-[8px] sm:text-[9px] text-center font-medium leading-tight line-clamp-2',\n },\n button.label\n ),\n ]\n );\n })\n )\n ),\n ]),\n ]);\n };\n\n const renderGridCells = (): (VNode | null)[] => {\n if (!currentPage.value) return [];\n const rendered = new Set<string>();\n const cells: (VNode | null)[] = [];\n\n currentPage.value.grid.forEach((row: (AACButton | null)[], rowIndex: number) => {\n row.forEach((button: AACButton | null, colIndex: number) => {\n if (!button) {\n cells.push(\n h('div', {\n key: `empty-${rowIndex}-${colIndex}`,\n class: 'aspect-square',\n })\n );\n return;\n }\n\n if (rendered.has(button.id)) {\n cells.push(null);\n return;\n }\n rendered.add(button.id);\n\n const buttonMetric = buttonMetricsLookup.value[button.id];\n const effort = buttonMetric?.effort || 0;\n const targetPageId = button.targetPageId || button.semanticAction?.targetId;\n const hasLink = targetPageId && props.tree.pages[targetPageId];\n const colSpan = button.columnSpan || 1;\n const rowSpan = button.rowSpan || 1;\n const predictions =\n button.predictions || (button.parameters as { predictions?: string[] })?.predictions;\n const hasPredictions = predictions && predictions.length > 0;\n const isPredictionCell =\n button.contentType === 'AutoContent' &&\n (button.contentSubType || '').toLowerCase() === 'prediction';\n const isWorkspace = button.contentType === 'Workspace';\n\n const isHighlighted =\n props.highlight &&\n props.highlight.pageId === currentPageId.value &&\n (props.highlight.buttonId === button.id ||\n (props.highlight.x !== undefined && props.highlight.y !== undefined\n ? button.x === props.highlight.x && button.y === props.highlight.y\n : false) ||\n (props.highlight.label && button.label === props.highlight.label));\n\n const params = button.parameters as {\n image_id?: string;\n gridPageName?: string;\n imageData?: Buffer;\n };\n\n let imageSrc: string | null = null;\n let apiUrl: string | undefined = undefined;\n\n const resolvedEntry = button.resolvedImageEntry;\n const buttonImage = button.image;\n\n if (\n resolvedEntry &&\n typeof resolvedEntry === 'string' &&\n resolvedEntry.startsWith('data:image/')\n ) {\n imageSrc = resolvedEntry;\n } else if (\n buttonImage &&\n typeof buttonImage === 'string' &&\n buttonImage.startsWith('data:image/')\n ) {\n imageSrc = buttonImage;\n } else if (\n resolvedEntry &&\n typeof resolvedEntry === 'string' &&\n !resolvedEntry.startsWith('[') &&\n !resolvedEntry.startsWith('data:image/')\n ) {\n if (props.loadId) {\n const entryPath = resolvedEntry;\n const pathMatch = entryPath.match(/^(?:Grids\\/)?(.+)$/);\n if (pathMatch) {\n apiUrl = `/api/image/${props.loadId}/${encodeURIComponent(pathMatch[1])}`;\n }\n } else {\n imageSrc = resolvedEntry;\n }\n } else if (\n buttonImage &&\n typeof buttonImage === 'string' &&\n !buttonImage.startsWith('[')\n ) {\n imageSrc = buttonImage;\n }\n\n if (\n props.loadId &&\n !imageSrc &&\n !apiUrl &&\n params &&\n params.image_id &&\n typeof params.image_id === 'string'\n ) {\n apiUrl = `/api/image/${props.loadId}/${encodeURIComponent(params.image_id)}`;\n }\n\n if (isWorkspace) {\n cells.push(\n h(\n 'div',\n {\n key: button.id,\n class:\n 'relative p-3 rounded-lg border-2 bg-gray-50 text-gray-800 flex items-center gap-2',\n style: {\n borderColor: button.style?.borderColor || '#e5e7eb',\n gridColumn: `${colIndex + 1} / span ${colSpan}`,\n gridRow: `${rowIndex + 1} / span ${rowSpan}`,\n },\n },\n [\n h('div', { class: 'font-semibold text-sm' }, button.label || 'Workspace'),\n h('div', { class: 'text-xs text-gray-500 truncate' }, message.value || 'Chat writing area'),\n ]\n )\n );\n return;\n }\n\n cells.push(\n h(\n 'button',\n {\n key: button.id,\n onClick: () => handleButtonClick(button),\n ref: isHighlighted\n ? ((el: Element | null) => {\n highlightedButtonRef.value = el as HTMLButtonElement | null;\n }) as VNodeRef\n : undefined,\n class:\n `relative aspect-square p-2 rounded-lg border-2 transition flex flex-col items-center justify-center gap-1 hover:opacity-80 hover:scale-105 active:scale-95 ${\n isHighlighted ? 'ring-4 ring-amber-400 ring-offset-2 ring-offset-white' : ''\n }`,\n style: {\n backgroundColor: button.style?.backgroundColor || '#f3f4f6',\n borderColor: button.style?.borderColor || '#e5e7eb',\n color:\n button.style?.fontColor || getTextColor(button.style?.backgroundColor),\n gridColumn: `${colIndex + 1} / span ${colSpan}`,\n gridRow: `${rowIndex + 1} / span ${rowSpan}`,\n },\n },\n [\n buttonMetric && props.showEffortBadges\n ? h(\n 'div',\n {\n class:\n 'absolute top-1 right-1 px-1.5 py-0.5 text-xs font-semibold rounded bg-blue-600 text-white shadow-sm',\n },\n effort.toFixed(1)\n )\n : null,\n hasLink && props.showLinkIndicators\n ? h('div', {\n class:\n 'absolute top-1 left-1 w-2 h-2 bg-green-500 rounded-full shadow-sm',\n })\n : null,\n buttonMetric?.pos && buttonMetric.pos !== 'Unknown' && buttonMetric.pos !== 'Ignore'\n ? h('div', {\n class: `absolute top-1 ${hasLink && props.showLinkIndicators ? 'left-4' : 'left-1'} px-1 py-0 text-[8px] font-semibold rounded shadow-sm text-white ${\n buttonMetric.pos === 'Verb' ? 'bg-orange-500' :\n buttonMetric.pos === 'Noun' ? 'bg-teal-500' :\n buttonMetric.pos === 'Pronoun' ? 'bg-pink-500' :\n buttonMetric.pos === 'Adjective' ? 'bg-yellow-500' :\n 'bg-gray-500'\n }`,\n title: `Part of speech: ${buttonMetric.pos}`,\n }, buttonMetric.pos.charAt(0))\n : null,\n hasPredictions\n ? h(\n 'div',\n {\n onClick: (event: MouseEvent) => handleShowPredictions(button, event),\n class:\n 'absolute bottom-1 right-1 px-1.5 py-0.5 text-xs font-semibold rounded bg-purple-600 text-white shadow-sm cursor-pointer hover:bg-purple-700 transition',\n title: `Has ${predictions?.length} word form${\n predictions && predictions.length > 1 ? 's' : ''\n }`,\n },\n predictions?.length\n )\n : null,\n imageSrc || apiUrl\n ? h('img', {\n src: imageSrc || apiUrl,\n alt: button.label,\n class: 'max-h-12 object-contain',\n onError: (e: Event) => {\n const target = e.target as HTMLImageElement;\n console.warn('Image failed to load:', button.label, 'src:', target.src);\n target.style.display = 'none';\n },\n })\n : null,\n h('div', { class: 'flex flex-col items-center justify-center' }, [\n h(\n 'span',\n {\n class: 'text-xs sm:text-sm text-center font-medium leading-tight line-clamp-3',\n },\n button.label\n ),\n isPredictionCell && predictions && predictions.length > 0\n ? h(\n 'div',\n {\n class:\n 'mt-1 text-[10px] sm:text-xs text-center opacity-80 space-y-0.5',\n },\n [\n ...predictions.slice(0, 3).map((prediction: string, idx: number) =>\n h('div', { key: `${button.id}-pred-${idx}` }, prediction)\n ),\n predictions.length > 3 ? h('div', '…') : null,\n ]\n )\n : null,\n ]),\n button.message && button.message !== button.label && !isPredictionCell\n ? h(\n 'span',\n { class: 'text-[10px] sm:text-xs text-center opacity-75 line-clamp-2' },\n button.message\n )\n : null,\n ]\n )\n );\n });\n });\n\n return cells;\n };\n\n return () => {\n if (!currentPage.value) {\n return h(\n 'div',\n {\n class: `flex items-center justify-center h-96 bg-gray-100 dark:bg-gray-800 rounded-lg ${\n props.className\n }`,\n },\n [\n h('div', { class: 'text-center' }, [\n h('p', { class: 'text-gray-600 dark:text-gray-400' }, 'No pages available'),\n ]),\n ]\n );\n }\n\n const toolbarPage = props.tree.toolbarId ? props.tree.pages[props.tree.toolbarId] : null;\n const gridRows = currentPage.value.grid.length;\n const gridCols = gridRows > 0 ? currentPage.value.grid[0].length : 0;\n\n return h('div', {\n class: `bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden ${props.className}`,\n }, [\n props.showMessageBar\n ? h(\n 'div',\n {\n class:\n 'p-4 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/50',\n },\n [\n h('div', { class: 'flex items-start justify-between gap-4' }, [\n h('div', { class: 'flex-1 min-w-0' }, [\n message.value\n ? h('div', { class: 'space-y-2' }, [\n h(\n 'p',\n { class: 'text-lg text-gray-900 dark:text-white break-words' },\n message.value\n ),\n h('div', { class: 'flex gap-4 text-sm' }, [\n h(\n 'div',\n { class: 'text-gray-600 dark:text-gray-400' },\n `${currentWordCount.value} ${\n currentWordCount.value === 1 ? 'word' : 'words'\n }`\n ),\n props.buttonMetrics\n ? [\n h(\n 'div',\n { class: 'text-gray-600 dark:text-gray-400' },\n [\n 'Effort: ',\n h(\n 'span',\n { class: 'font-medium' },\n currentEffort.value.toFixed(2)\n ),\n ]\n ),\n h(\n 'div',\n { class: 'text-gray-600 dark:text-gray-400' },\n [\n 'Avg: ',\n h(\n 'span',\n { class: 'font-medium' },\n currentWordCount.value > 0\n ? (currentEffort.value / currentWordCount.value).toFixed(2)\n : '0.00'\n ),\n ]\n ),\n ]\n : null,\n ]),\n ])\n : h(\n 'p',\n { class: 'text-gray-500 dark:text-gray-400 italic' },\n 'Tap buttons to build a sentence...'\n ),\n ]),\n message.value\n ? h(\n 'button',\n {\n onClick: clearMessage,\n class:\n 'p-2 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-lg transition',\n 'aria-label': 'Clear message',\n },\n [\n h(\n 'svg',\n {\n class: 'h-5 w-5 text-gray-600 dark:text-gray-400',\n fill: 'none',\n stroke: 'currentColor',\n viewBox: '0 0 24 24',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'stroke-width': 2,\n d: 'M6 18L18 6M6 6l12 12',\n }),\n ]\n ),\n ]\n )\n : null,\n ]),\n ]\n )\n : null,\n h('div', { class: 'flex' }, [\n renderToolbar(toolbarPage),\n h('div', { class: 'flex-1' }, [\n h('div', {\n class: 'flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700',\n }, [\n h('div', { class: 'flex items-center gap-2' }, [\n pageHistory.value.length > 0\n ? h(\n 'button',\n {\n onClick: handleBack,\n class: 'p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition',\n 'aria-label': 'Go back',\n },\n [\n h(\n 'svg',\n {\n class: 'h-5 w-5 text-gray-600 dark:text-gray-400',\n fill: 'none',\n stroke: 'currentColor',\n viewBox: '0 0 24 24',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'stroke-width': 2,\n d: 'M15 19l-7-7 7-7',\n }),\n ]\n ),\n ]\n )\n : null,\n h(\n 'h3',\n { class: 'text-lg font-semibold text-gray-900 dark:text-white' },\n currentPage.value.name\n ),\n ]),\n h(\n 'div',\n { class: 'text-sm text-gray-500 dark:text-gray-400' },\n `${gridRows}×${gridCols} grid`\n ),\n ]),\n h(\n 'div',\n {\n class: 'p-4 gap-2 overflow-auto max-h-[600px]',\n style: {\n display: 'grid',\n gridTemplateColumns: `repeat(${gridCols}, minmax(0, 1fr))`,\n },\n },\n renderGridCells()\n ),\n Object.keys(props.tree.pages).length > 1\n ? h('div', { class: 'p-4 border-t border-gray-200 dark:border-gray-700' }, [\n h(\n 'p',\n { class: 'text-sm text-gray-600 dark:text-gray-400 mb-2' },\n `${Object.keys(props.tree.pages).length} pages in this vocabulary`\n ),\n ])\n : null,\n ]),\n ]),\n predictionsTooltip.value\n ? h(PredictionsTooltip, {\n predictions: predictionsTooltip.value.predictions,\n label: predictionsTooltip.value.label,\n position: predictionsTooltip.value.position,\n buttonMetricsLookup: predictionsTooltip.value.buttonMetricsLookup,\n onClose: predictionsTooltip.value.onClose,\n onWordClick: predictionsTooltip.value.onWordClick,\n pos: predictionsTooltip.value.pos,\n })\n : null,\n ]);\n };\n },\n});\n\nexport default BoardViewer;\n"],"mappings":"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","names":[]}
package/dist/vue.d.mts CHANGED
@@ -3,8 +3,8 @@ import { VNode } from 'vue';
3
3
  import * as _willwade_aac_processors from '@willwade/aac-processors';
4
4
  import { AACButton } from '@willwade/aac-processors';
5
5
  export { AACButton, AACPage, AACTree } from '@willwade/aac-processors';
6
- import { B as ButtonMetric } from './index-BL1tS3BY.mjs';
7
- export { a as BoardViewerProps } from './index-BL1tS3BY.mjs';
6
+ import { B as ButtonMetric } from './index-B783kPn6.mjs';
7
+ export { a as BoardViewerProps } from './index-B783kPn6.mjs';
8
8
 
9
9
  declare const BoardViewer: vue.DefineComponent<{
10
10
  readonly tree: _willwade_aac_processors.AACTree;
package/dist/vue.d.ts CHANGED
@@ -3,8 +3,8 @@ import { VNode } from 'vue';
3
3
  import * as _willwade_aac_processors from '@willwade/aac-processors';
4
4
  import { AACButton } from '@willwade/aac-processors';
5
5
  export { AACButton, AACPage, AACTree } from '@willwade/aac-processors';
6
- import { B as ButtonMetric } from './index-BL1tS3BY.js';
7
- export { a as BoardViewerProps } from './index-BL1tS3BY.js';
6
+ import { B as ButtonMetric } from './index-B783kPn6.js';
7
+ export { a as BoardViewerProps } from './index-B783kPn6.js';
8
8
 
9
9
  declare const BoardViewer: vue.DefineComponent<{
10
10
  readonly tree: _willwade_aac_processors.AACTree;