tabulark 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +67 -2
  2. package/README.md +125 -42
  3. package/dist/client.d.ts +71 -2
  4. package/dist/errors.d.ts +7 -1
  5. package/dist/experimental.js +1 -1
  6. package/dist/experimental.js.map +2 -2
  7. package/dist/http.d.ts +65 -0
  8. package/dist/http.js +2 -0
  9. package/dist/http.js.map +7 -0
  10. package/dist/index.d.ts +2 -1
  11. package/dist/index.js +2 -1
  12. package/dist/index.js.map +4 -4
  13. package/dist/model.d.ts +6 -3
  14. package/dist/protocol.d.ts +3 -3
  15. package/dist/range-cache.d.ts +24 -5
  16. package/dist/range-source.d.ts +64 -0
  17. package/dist/rpc-client.d.ts +15 -2
  18. package/dist/source.d.ts +13 -0
  19. package/dist/view/canvas-table-view-public.d.ts +6 -0
  20. package/dist/view/canvas-table-view.d.ts +8 -0
  21. package/dist/wasm/arrow/tabulark_arrow.d.ts +33 -6
  22. package/dist/wasm/arrow/tabulark_arrow.js +129 -17
  23. package/dist/wasm/arrow/tabulark_arrow_bg.wasm +0 -0
  24. package/dist/wasm/arrow/tabulark_arrow_bg.wasm.d.ts +6 -1
  25. package/dist/wasm/delimited/tabulark_delimited.d.ts +31 -5
  26. package/dist/wasm/delimited/tabulark_delimited.js +115 -8
  27. package/dist/wasm/delimited/tabulark_delimited_bg.wasm +0 -0
  28. package/dist/wasm/delimited/tabulark_delimited_bg.wasm.d.ts +6 -1
  29. package/dist/wasm/excel/tabulark_excel.d.ts +33 -8
  30. package/dist/wasm/excel/tabulark_excel.js +129 -19
  31. package/dist/wasm/excel/tabulark_excel_bg.wasm +0 -0
  32. package/dist/wasm/excel/tabulark_excel_bg.wasm.d.ts +6 -1
  33. package/dist/wasm/parquet/tabulark_parquet.d.ts +31 -6
  34. package/dist/wasm/parquet/tabulark_parquet.js +135 -17
  35. package/dist/wasm/parquet/tabulark_parquet_bg.wasm +0 -0
  36. package/dist/wasm/parquet/tabulark_parquet_bg.wasm.d.ts +6 -1
  37. package/dist/worker/blob-source-accessor.d.ts +10 -0
  38. package/dist/worker/source-accessor.d.ts +76 -0
  39. package/dist/worker/wasm-adapter.d.ts +39 -38
  40. package/dist/worker-range-source.d.ts +1 -0
  41. package/dist/worker-range-source.js +2 -0
  42. package/dist/worker-range-source.js.map +7 -0
  43. package/dist/worker.js +1 -1
  44. package/dist/worker.js.map +4 -4
  45. package/package.json +8 -1
@@ -1,2 +1,2 @@
1
- var DEFAULT_CANVAS_TABLE_THEME=Object.freeze({background:"#ffffff",foreground:"#172033",mutedForeground:"#667085",headerBackground:"#f2f4f7",headerForeground:"#344054",alternateRowBackground:"#f9fafb",gridLine:"#d0d5dd",selectionBackground:"rgba(21, 112, 239, 0.14)",selectionBorder:"#1570ef",activeCellBorder:"#175cd3",loadingBackground:"#e4e7ec",font:'13px ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif',headerFont:'600 13px ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif'});var CanvasTablePainter=class{canvas;#context;#theme;#maxDevicePixelRatio;#forcedColors;#cssWidth=0;#cssHeight=0;#devicePixelRatio=1;constructor(canvas,options={}){const context=canvas.getContext("2d",{alpha:false});if(context===null){throw new Error("Tabulark Canvas rendering requires a 2D rendering context")}this.canvas=canvas;this.#context=context;this.#theme=Object.freeze({...DEFAULT_CANVAS_TABLE_THEME,...options.theme});this.#maxDevicePixelRatio=positive(options.maxDevicePixelRatio,Number.POSITIVE_INFINITY);this.#forcedColors=options.forcedColors??false}setTheme(theme,options={}){this.#theme=Object.freeze({...theme});this.#forcedColors=options.forcedColors??false}paint(snapshot){this.#resize(snapshot.width,snapshot.height);const context=this.#context;const theme=this.#theme;context.save();context.setTransform(this.#devicePixelRatio,0,0,this.#devicePixelRatio,0,0);context.clearRect(0,0,snapshot.width,snapshot.height);context.fillStyle=theme.background;context.fillRect(0,0,snapshot.width,snapshot.height);this.#paintRows(snapshot);this.#paintHeader(snapshot);this.#paintGrid(snapshot);this.#paintMergedCells(snapshot);this.#paintSelection(snapshot);context.restore()}#resize(width,height){const view=this.canvas.ownerDocument.defaultView;const browserRatio=view?.devicePixelRatio??1;const ratio=Math.max(1,Math.min(browserRatio,this.#maxDevicePixelRatio));const cssWidth=Math.max(0,Math.floor(width));const cssHeight=Math.max(0,Math.floor(height));const pixelWidth=Math.max(1,Math.round(cssWidth*ratio));const pixelHeight=Math.max(1,Math.round(cssHeight*ratio));if(cssWidth===this.#cssWidth&&cssHeight===this.#cssHeight&&ratio===this.#devicePixelRatio&&this.canvas.width===pixelWidth&&this.canvas.height===pixelHeight){return}this.#cssWidth=cssWidth;this.#cssHeight=cssHeight;this.#devicePixelRatio=ratio;this.canvas.width=pixelWidth;this.canvas.height=pixelHeight;this.canvas.style.width=`${cssWidth}px`;this.canvas.style.height=`${cssHeight}px`}#paintRows(snapshot){const context=this.#context;const theme=this.#theme;const columnsByIndex=new Map(snapshot.columns.map(column=>[column.index,column]));context.font=theme.font;context.textBaseline="middle";const rows=[...snapshot.rows.filter(row=>row.frozen!==true),...snapshot.rows.filter(row=>row.frozen===true)];for(const row of rows){if(row.y+row.height<=snapshot.headerHeight||row.y>=snapshot.height){continue}context.fillStyle=row.index%2===0?theme.background:theme.alternateRowBackground;context.fillRect(0,row.y,snapshot.width,row.height);context.fillStyle=theme.mutedForeground;context.textAlign="right";context.fillText(String(row.index+1),snapshot.rowGutterWidth-10,row.y+row.height/2);const cells=[...row.cells.filter(cell=>columnsByIndex.get(cell.columnIndex)?.frozen!==true),...row.cells.filter(cell=>columnsByIndex.get(cell.columnIndex)?.frozen===true)];for(const cell of cells){const column=columnsByIndex.get(cell.columnIndex);if(column===void 0||column.x+column.width<=snapshot.rowGutterWidth||cell.coveredByMerge===true){continue}this.#paintCellValue(cell.value,column.x,row.y,column.width,row.height,cell.style)}}}#paintCellValue(value,x,y,width,height,style){const context=this.#context;const theme=this.#theme;const horizontalPadding=10;if(width<=horizontalPadding*2){return}context.save();context.beginPath();context.rect(x+1,y+1,Math.max(0,width-2),Math.max(0,height-2));context.clip();this.#paintCellFill(x,y,width,height,style);this.#applyCellFont(style);context.textAlign=horizontalAlignment(style);context.textBaseline=verticalBaseline(style);if(value===void 0){context.fillStyle=theme.loadingBackground;const skeletonWidth=Math.max(12,Math.min(width-horizontalPadding*2,width*.42));context.fillRect(x+horizontalPadding,y+height/2-3,skeletonWidth,6)}else{context.fillStyle=value===null?theme.mutedForeground:this.#cellForeground(style,theme.foreground);const label=value===null?"\u2205":value;const textX=horizontalTextX(context.textAlign,x,width,horizontalPadding);const textY=verticalTextY(context.textBaseline,y,height,4);context.fillText(label,textX,textY,Math.max(0,width-horizontalPadding*2));if(style?.font?.underline===true){const measured=Math.min(context.measureText(label).width,width-horizontalPadding*2);const start=underlineStart(context.textAlign,textX,measured);context.strokeStyle=String(context.fillStyle);context.lineWidth=1;context.beginPath();context.moveTo(start,Math.min(y+height-4,textY+3));context.lineTo(start+measured,Math.min(y+height-4,textY+3));context.stroke()}}this.#paintCellBorders(x,y,width,height,style);context.restore()}#paintMergedCells(snapshot){for(const merged of snapshot.mergedCells??[]){if(merged.width<=0||merged.height<=0||merged.x+merged.width<=snapshot.rowGutterWidth||merged.y+merged.height<=snapshot.headerHeight||merged.x>=snapshot.width||merged.y>=snapshot.height){continue}this.#context.fillStyle=merged.region.rowStart%2===0?this.#theme.background:this.#theme.alternateRowBackground;this.#context.fillRect(merged.x,merged.y,merged.width,merged.height);if(merged.continuation===true){this.#paintCellFill(merged.x,merged.y,merged.width,merged.height,merged.style);this.#paintCellBorders(merged.x,merged.y,merged.width,merged.height,merged.style)}else{this.#paintCellValue(merged.value,merged.x,merged.y,merged.width,merged.height,merged.style)}this.#context.strokeStyle=this.#theme.gridLine;this.#context.lineWidth=1/this.#devicePixelRatio;this.#context.strokeRect(merged.x+.5/this.#devicePixelRatio,merged.y+.5/this.#devicePixelRatio,Math.max(0,merged.width-1/this.#devicePixelRatio),Math.max(0,merged.height-1/this.#devicePixelRatio))}}#paintCellFill(x,y,width,height,style){if(this.#forcedColors||style===void 0)return;const color=cssColor(style.fillColor??style.backgroundColor);if(color===void 0)return;this.#context.fillStyle=color;this.#context.fillRect(x+1,y+1,Math.max(0,width-2),Math.max(0,height-2))}#applyCellFont(style){if(style?.font===void 0){this.#context.font=this.#theme.font;return}const font=style.font;const size=finitePositive(font.size)??13;const family=font.family&&font.family.trim().length>0?font.family:'ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif';this.#context.font=`${font.italic===true?"italic ":""}${font.bold===true?"700 ":"400 "}${size}px ${family}`}#cellForeground(style,fallback){if(this.#forcedColors)return fallback;return cssColor(style?.font?.color)??cssColor(style?.foregroundColor)??fallback}#paintCellBorders(x,y,width,height,style){if(style?.borders===void 0)return;const context=this.#context;const sides=style.borders;const paint=(side,startX,startY,endX,endY)=>{const border=sides[side];if(border===void 0||border.style==="none")return;context.save();const widthForStyle=borderWidth(border.style);context.lineWidth=widthForStyle;context.strokeStyle=this.#forcedColors?this.#theme.gridLine:cssColor(border.color)??this.#theme.gridLine;context.setLineDash(borderDash(border.style));context.beginPath();context.moveTo(startX,startY);context.lineTo(endX,endY);context.stroke();if(border.style==="double"){const offset=Math.max(2,widthForStyle+1);const horizontal=startY===endY;context.beginPath();context.moveTo(horizontal?startX:startX+offset,horizontal?startY+offset:startY);context.lineTo(horizontal?endX:endX+offset,horizontal?endY+offset:endY);context.stroke()}context.restore()};paint("top",x+.5,y+.5,x+width-.5,y+.5);paint("right",x+width-.5,y+.5,x+width-.5,y+height-.5);paint("bottom",x+.5,y+height-.5,x+width-.5,y+height-.5);paint("left",x+.5,y+.5,x+.5,y+height-.5)}#paintHeader(snapshot){const context=this.#context;const theme=this.#theme;context.fillStyle=theme.headerBackground;context.fillRect(0,0,snapshot.width,snapshot.headerHeight);context.font=theme.headerFont;context.fillStyle=theme.headerForeground;context.textBaseline="middle";context.textAlign="left";const columns=[...snapshot.columns.filter(column=>column.frozen!==true),...snapshot.columns.filter(column=>column.frozen===true)];for(const column of columns){if(column.x+column.width<=snapshot.rowGutterWidth||column.x>=snapshot.width){continue}context.save();context.beginPath();context.rect(column.x+1,0,Math.max(0,column.width-2),snapshot.headerHeight);context.clip();context.fillText(column.name,column.x+10,snapshot.headerHeight/2);context.restore()}context.textAlign="center";context.fillText("#",snapshot.rowGutterWidth/2,snapshot.headerHeight/2,snapshot.rowGutterWidth)}#paintGrid(snapshot){const context=this.#context;const theme=this.#theme;context.strokeStyle=theme.gridLine;context.lineWidth=1/this.#devicePixelRatio;context.beginPath();const snap=.5/this.#devicePixelRatio;context.moveTo(snapshot.rowGutterWidth+snap,0);context.lineTo(snapshot.rowGutterWidth+snap,snapshot.height);context.moveTo(0,snapshot.headerHeight+snap);context.lineTo(snapshot.width,snapshot.headerHeight+snap);for(const column of snapshot.columns){const x=column.x+column.width+snap;if(x>snapshot.rowGutterWidth&&x<snapshot.width){context.moveTo(x,0);context.lineTo(x,snapshot.height)}}for(const row of snapshot.rows){const y=row.y+row.height+snap;if(y>snapshot.headerHeight&&y<snapshot.height){context.moveTo(0,y);context.lineTo(snapshot.width,y)}}context.stroke()}#paintSelection(snapshot){const selection=snapshot.selection;const context=this.#context;const theme=this.#theme;if(selection!==void 0){const selectedColumns=snapshot.columns.filter(column=>column.index>=selection.columnStart&&column.index<selection.columnEnd);const selectedRows=snapshot.rows.filter(row=>row.index>=selection.rowStart&&row.index<selection.rowEnd);if(selectedColumns.length>0&&selectedRows.length>0){const firstColumn=selectedColumns[0];const lastColumn=selectedColumns.at(-1);const firstRow=selectedRows[0];const lastRow=selectedRows.at(-1);const x=Math.max(snapshot.rowGutterWidth,firstColumn.x);const y=Math.max(snapshot.headerHeight,firstRow.y);const right=Math.min(snapshot.width,lastColumn.x+lastColumn.width);const bottom=Math.min(snapshot.height,lastRow.y+lastRow.height);if(right>x&&bottom>y){context.save();context.fillStyle=theme.selectionBackground;if(this.#forcedColors){context.globalAlpha=.28}context.fillRect(x,y,right-x,bottom-y);context.restore();context.strokeStyle=theme.selectionBorder;context.lineWidth=this.#forcedColors?2:1;context.setLineDash(this.#forcedColors?[4,2]:[]);context.strokeRect(x+context.lineWidth/2,y+context.lineWidth/2,Math.max(0,right-x-context.lineWidth),Math.max(0,bottom-y-context.lineWidth));context.setLineDash([])}}}const activeMerges=snapshot.activeCell===void 0?[]:snapshot.mergedCells?.filter(merged=>snapshot.activeCell.rowIndex>=merged.region.rowStart&&snapshot.activeCell.rowIndex<merged.region.rowEnd&&snapshot.activeCell.columnIndex>=merged.region.columnStart&&snapshot.activeCell.columnIndex<merged.region.columnEnd)??[];const activeColumn=snapshot.activeCell===void 0?void 0:snapshot.columns.find(column=>column.index===snapshot.activeCell.columnIndex);const activeRow=snapshot.activeCell===void 0?void 0:snapshot.rows.find(row=>row.index===snapshot.activeCell.rowIndex);const activeRects=activeMerges.length>0?activeMerges:activeColumn!==void 0&&activeRow!==void 0?[{x:activeColumn.x,y:activeRow.y,width:activeColumn.width,height:activeRow.height}]:[];for(const activeRect of activeRects){const activeX=Math.max(snapshot.rowGutterWidth,activeRect.x);const activeY=Math.max(snapshot.headerHeight,activeRect.y);const activeRight=Math.min(snapshot.width,activeRect.x+activeRect.width);const activeBottom=Math.min(snapshot.height,activeRect.y+activeRect.height);context.strokeStyle=theme.activeCellBorder;context.lineWidth=this.#forcedColors?3:2;context.setLineDash([]);context.strokeRect(activeX+context.lineWidth/2,activeY+context.lineWidth/2,Math.max(0,activeRight-activeX-context.lineWidth),Math.max(0,activeBottom-activeY-context.lineWidth))}}};function cssColor(color){const value=color?.css?.trim();if(!value)return void 0;const supports=globalThis.CSS?.supports;return typeof supports!=="function"||supports("color",value)?value:void 0}function finitePositive(value){return value!==void 0&&Number.isFinite(value)&&value>0?value:void 0}function horizontalAlignment(style){switch(style?.horizontalAlignment){case"center":return"center";case"right":return"right";case"left":case"justify":case"general":default:return"left"}}function verticalBaseline(style){switch(style?.verticalAlignment){case"top":return"top";case"bottom":return"bottom";case"center":case"justify":default:return"middle"}}function horizontalTextX(alignment,x,width,padding){if(alignment==="center")return x+width/2;if(alignment==="right"||alignment==="end")return x+width-padding;return x+padding}function verticalTextY(baseline,y,height,padding){if(baseline==="top"||baseline==="hanging")return y+padding;if(baseline==="bottom"||baseline==="ideographic")return y+height-padding;return y+height/2}function underlineStart(alignment,textX,width){if(alignment==="center")return textX-width/2;if(alignment==="right"||alignment==="end")return textX-width;return textX}function borderWidth(style){switch(style){case"medium":return 2;case"thick":return 3;case"double":return 1;default:return 1}}function borderDash(style){if(style==="dashed")return[4,2];if(style==="dotted")return[1,2];return[]}function positive(value,fallback){return value!==void 0&&Number.isFinite(value)&&value>0?value:fallback}var TabularkError=class _TabularkError extends Error{code;retryable;details;constructor(code,message,options={}){super(message,{cause:options.cause});this.name="TabularkError";this.code=code;this.retryable=options.retryable??false;this.details=code==="RESOURCE_LIMIT"?normalizeResourceLimitDetails(options.details):options.details}static fromSerialized(error){return new _TabularkError(error.code,error.message,{retryable:error.retryable,details:error.details})}};function normalizeResourceLimitDetails(details){const raw=isRecord(details)?details:{};const resource=typeof raw.resource==="string"&&raw.resource.length>0?raw.resource:typeof raw.resourceCategory==="string"&&raw.resourceCategory.length>0?raw.resourceCategory:inferResource(raw);if(isQuantity(raw.requiredBytes)&&isQuantity(raw.availableBytes)){return Object.freeze({...raw,resource})}if(isQuantity(raw.required)&&isQuantity(raw.available)){return Object.freeze({...raw,resource})}const byteLimit=firstQuantity(raw,["maxDecodedBytes","maxOutputBytes","maxMetadataBytes","maxBlockBytes","maxDisplayCellBytes","maxDisplayBytes","maxSourceBytes","maxFieldBytes","maxBatchBytes","maxOperationBytes","maxZipEntryBytes","maxZipUncompressedBytes","maxCfbStreamBytes"]);if(byteLimit!==void 0){const requiredBytes=firstQuantity(raw,["decodedBytes","outputBytes","metadataBytes","blockBytes","displayCellBytes","displayBytes","sourceBytes","fieldBytes","batchBytes","operationBytes"])??byteLimit+1;return Object.freeze({...raw,resource,requiredBytes,availableBytes:byteLimit})}const available=firstQuantity(raw,["limit","maxCells","maxRangeCells","maxSources","maxActiveRanges","maxRangeWaiters","maxColumns","maxFields","maxNestingDepth","maxWorksheetRows","maxWorksheetColumns","maxStyles","maxMergedRegions"])??0;const required=firstQuantity(raw,["cells","cellCount","visibleCellCount","sources","activeRanges","rangeWaiters","columns","fields","nestingDepth","worksheetRows","worksheetColumns","styles","mergedRegions"])??available+1;return Object.freeze({...raw,resource,required,available})}function inferResource(details){const keys=Object.keys(details).join(" ").toLowerCase();if(keys.includes("source"))return keys.includes("byte")?"source-staging":"source-slots";if(keys.includes("metadata"))return"metadata";if(keys.includes("decoded")||keys.includes("decompress"))return"decompression";if(keys.includes("display"))return"display-values";if(keys.includes("field"))return"field";if(keys.includes("batch"))return"batch";if(keys.includes("range")||keys.includes("cell"))return"range-cells";if(keys.includes("column"))return"columns";if(keys.includes("nest"))return"descriptor-nesting";if(keys.includes("worksheet"))return"worksheet-dimensions";return"adapter-resource"}function firstQuantity(details,names){for(const name of names){if(isQuantity(details[name]))return details[name]}return void 0}function isQuantity(value){return typeof value==="number"&&Number.isFinite(value)&&value>=0}function isRecord(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function closedError(resource){return new TabularkError("HANDLE_CLOSED",`${resource} is closed`)}function invalidArgument(message,details){return new TabularkError("INVALID_ARGUMENT",message,{details})}var DEFAULT_MEMORY_BUDGET_BYTES=256*1024*1024;var MAX_ARRAY_BUFFER_BYTES=128*1024*1024;var MAX_RANGE_CELLS=25e4;var INDEX_BUDGET_MAX_BYTES=64*1024*1024;var WORKER_RANGE_CACHE_MAX_BYTES=96*1024*1024;var MAIN_THREAD_RANGE_CACHE_MAX_BYTES=32*1024*1024;var FIELD_AND_BATCH_MAX_BYTES=8*1024*1024;var DEFAULT_ROW_HEIGHT=28;var DEFAULT_HEADER_HEIGHT=36;var DEFAULT_ROW_HEADER_WIDTH=64;var DEFAULT_COLUMN_WIDTH=160;var DEFAULT_MIN_COLUMN_WIDTH=64;var DEFAULT_MAX_COLUMN_WIDTH=640;var DEFAULT_SCROLL_PIXEL_LIMIT=16e6;var DEFAULT_OVERSCAN_ROWS=8;var DEFAULT_OVERSCAN_COLUMNS=1;function createTableLayout(metadata,columnWidths,viewport,options={}){const rowHeight=positiveFinite(options.rowHeight??DEFAULT_ROW_HEIGHT,"rowHeight");const headerHeight=nonNegativeFinite(options.headerHeight??DEFAULT_HEADER_HEIGHT,"headerHeight");const rowHeaderWidth=nonNegativeFinite(options.rowHeaderWidth??DEFAULT_ROW_HEADER_WIDTH,"rowHeaderWidth");const scrollPixelLimit=positiveFinite(options.scrollPixelLimit??DEFAULT_SCROLL_PIXEL_LIMIT,"scrollPixelLimit");const overscanRows=nonNegativeInteger(options.overscanRows??DEFAULT_OVERSCAN_ROWS,"overscanRows");const overscanColumns=nonNegativeInteger(options.overscanColumns??DEFAULT_OVERSCAN_COLUMNS,"overscanColumns");const width=nonNegativeFinite(viewport.width,"viewport.width");const height=nonNegativeFinite(viewport.height,"viewport.height");const devicePixelRatio=positiveFinite(viewport.devicePixelRatio??1,"viewport.devicePixelRatio");const columnCount=metadata.schema.columns.length;if(columnWidths.length!==columnCount){throw invalidArgument(`columnWidths has ${columnWidths.length} entries; expected ${columnCount}`)}for(let index=0;index<columnWidths.length;index+=1){positiveFinite(columnWidths[index],`columnWidths[${index}]`)}const hiddenColumns=options.hiddenColumns??[];if(hiddenColumns.length!==0&&hiddenColumns.length!==columnCount){throw invalidArgument(`hiddenColumns has ${hiddenColumns.length} entries; expected ${columnCount}`)}const effectiveColumnWidths=Object.freeze(columnWidths.map((width2,index)=>hiddenColumns[index]===true?0:width2));const rowCount=extentValue(metadata.extent.rows);const frozenRowCount=Math.min(rowCount,nonNegativeInteger(options.frozenRows??0,"frozenRows"));const frozenColumnCount=Math.min(columnCount,nonNegativeInteger(options.frozenColumns??0,"frozenColumns"));const bodyWidth=Math.max(0,width-rowHeaderWidth);const bodyHeight=Math.max(0,height-headerHeight);const prefixes=columnOffsets(effectiveColumnWidths);const rowGeometry=createSparseAxisGeometry(rowCount,rowHeight,options.rowEntries??[]);const logicalWidth=prefixes[prefixes.length-1]??0;const logicalHeight=rowGeometry.contentSize;const horizontal=createScrollAxis(logicalWidth,bodyWidth,viewport.scrollLeft,scrollPixelLimit);const vertical=createScrollAxis(logicalHeight,bodyHeight,viewport.scrollTop,scrollPixelLimit);const visibleRows=visibleRowRange(rowGeometry,vertical.logicalOffset,bodyHeight);const visibleColumns=visibleColumnRange(prefixes,horizontal.logicalOffset,bodyWidth);const overscanRowRange=expandRange(visibleRows,overscanRows,rowCount);const overscanColumnRange=expandRange(visibleColumns,overscanColumns,columnCount);const frozenRowExtent=axisPosition(rowGeometry,frozenRowCount);const frozenColumnExtent=prefixes[frozenColumnCount]??0;const frozenRowRange=frozenRowCount===0?Object.freeze({start:0,end:0}):clampRangeEnd(visibleRowRange(rowGeometry,0,Math.min(bodyHeight,frozenRowExtent)),frozenRowCount);const frozenColumnRange=frozenColumnCount===0?Object.freeze({start:0,end:0}):clampRangeEnd(visibleColumnRange(prefixes,0,Math.min(bodyWidth,frozenColumnExtent)),frozenColumnCount);return Object.freeze({width,height,devicePixelRatio,rowHeight,headerHeight,rowHeaderWidth,bodyWidth,bodyHeight,rowCount,columnCount,rows:Object.freeze({visible:visibleRows,overscan:overscanRowRange}),columns:Object.freeze({visible:visibleColumns,overscan:overscanColumnRange}),visibleColumns:materializeColumns(metadata,effectiveColumnWidths,prefixes,visibleColumns,frozenColumnRange,rowHeaderWidth,horizontal.logicalOffset,frozenColumnCount),overscanColumns:materializeColumns(metadata,effectiveColumnWidths,prefixes,overscanColumnRange,frozenColumnRange,rowHeaderWidth,horizontal.logicalOffset,frozenColumnCount),visibleRows:materializeRows(rowGeometry,visibleRows,frozenRowRange,headerHeight,vertical.logicalOffset,frozenRowCount),overscanRows:materializeRows(rowGeometry,overscanRowRange,frozenRowRange,headerHeight,vertical.logicalOffset,frozenRowCount),effectiveColumnWidths,rowGeometry,frozenRowCount,frozenColumnCount,frozenRowExtent,frozenColumnExtent,frozenRowRange,frozenColumnRange,horizontal,vertical,spacerWidth:rowHeaderWidth+horizontal.physicalContentSize,spacerHeight:headerHeight+vertical.physicalContentSize})}function createScrollAxis(logicalContentSize,logicalViewportSize,physicalOffset,pixelLimit=DEFAULT_SCROLL_PIXEL_LIMIT){nonNegativeFinite(logicalContentSize,"logicalContentSize");nonNegativeFinite(logicalViewportSize,"logicalViewportSize");positiveFinite(pixelLimit,"pixelLimit");const logicalMaxOffset=Math.max(0,logicalContentSize-logicalViewportSize);const largestUsableSize=Math.max(pixelLimit,logicalViewportSize+1);const physicalContentSize=Math.min(logicalContentSize,largestUsableSize);const physicalMaxOffset=Math.max(0,physicalContentSize-logicalViewportSize);const normalizedPhysicalOffset=clampFinite(physicalOffset,0,physicalMaxOffset);const compressed=logicalMaxOffset>physicalMaxOffset;const logicalOffset=physicalMaxOffset===0?0:compressed?normalizedPhysicalOffset/physicalMaxOffset*logicalMaxOffset:normalizedPhysicalOffset;return Object.freeze({logicalContentSize,logicalViewportSize,logicalOffset,logicalMaxOffset,physicalContentSize,physicalOffset:normalizedPhysicalOffset,physicalMaxOffset,compressed})}function logicalToPhysicalOffset(axis,logicalOffset){const normalized=clampFinite(logicalOffset,0,axis.logicalMaxOffset);if(!axis.compressed){return Math.min(normalized,axis.physicalMaxOffset)}return axis.logicalMaxOffset===0?0:normalized/axis.logicalMaxOffset*axis.physicalMaxOffset}function physicalToLogicalOffset(axis,physicalOffset){const normalized=clampFinite(physicalOffset,0,axis.physicalMaxOffset);if(!axis.compressed){return Math.min(normalized,axis.logicalMaxOffset)}return axis.physicalMaxOffset===0?0:normalized/axis.physicalMaxOffset*axis.logicalMaxOffset}function hitTest(layout,x,y,_columnWidths,resizeHandleWidth=6){if(!Number.isFinite(x)||!Number.isFinite(y)||x<0||y<0||x>=layout.width||y>=layout.height){return Object.freeze({kind:"outside"})}const columnWidths=layout.effectiveColumnWidths;if(x<layout.rowHeaderWidth&&y<layout.headerHeight){return Object.freeze({kind:"corner"})}if(y<layout.headerHeight){const offsets=columnOffsets(columnWidths);const logicalX=x-layout.rowHeaderWidth+layout.horizontal.logicalOffset;const nextBoundary=lowerBound(offsets,logicalX);const handleWidth=Math.max(0,resizeHandleWidth);for(const boundaryIndex of[nextBoundary,nextBoundary-1]){if(boundaryIndex<=0||boundaryIndex>=offsets.length){continue}const boundaryX=layout.rowHeaderWidth+offsets[boundaryIndex]-layout.horizontal.logicalOffset;if(Math.abs(x-boundaryX)<=handleWidth){return Object.freeze({kind:"column-resize",columnIndex:boundaryIndex-1,boundaryX})}}const columnIndex2=columnAtViewportX(layout,x,columnWidths);if(columnIndex2===null){return Object.freeze({kind:"outside"})}return Object.freeze({kind:"column-header",columnIndex:columnIndex2})}const rowIndex=rowAtViewportY(layout,y);if(rowIndex===null){return Object.freeze({kind:"outside"})}if(x<layout.rowHeaderWidth){return Object.freeze({kind:"row-header",rowIndex})}const columnIndex=columnAtViewportX(layout,x,columnWidths);return columnIndex===null?Object.freeze({kind:"outside"}):Object.freeze({kind:"cell",rowIndex,columnIndex})}function cellRect(layout,_columnWidths,rowIndex,columnIndex){const columnWidths=layout.effectiveColumnWidths;const offsets=columnOffsets(columnWidths);const rowOffset=axisPosition(layout.rowGeometry,rowIndex);const columnOffset=offsets[columnIndex]??0;return Object.freeze({x:layout.rowHeaderWidth+columnOffset-(columnIndex<layout.frozenColumnCount?0:layout.horizontal.logicalOffset),y:layout.headerHeight+rowOffset-(rowIndex<layout.frozenRowCount?0:layout.vertical.logicalOffset),width:columnWidths[columnIndex]??0,height:axisSize(layout.rowGeometry,rowIndex)})}function columnHeaderRect(layout,_columnWidths,columnIndex){const columnWidths=layout.effectiveColumnWidths;const offsets=columnOffsets(columnWidths);return Object.freeze({x:layout.rowHeaderWidth+offsets[columnIndex]-(columnIndex<layout.frozenColumnCount?0:layout.horizontal.logicalOffset),y:0,width:columnWidths[columnIndex]??0,height:layout.headerHeight})}function rowHeaderRect(layout,rowIndex){const rowOffset=axisPosition(layout.rowGeometry,rowIndex);return Object.freeze({x:0,y:layout.headerHeight+rowOffset-(rowIndex<layout.frozenRowCount?0:layout.vertical.logicalOffset),width:layout.rowHeaderWidth,height:axisSize(layout.rowGeometry,rowIndex)})}function selectionRect(layout,_columnWidths,range){const columnWidths=layout.effectiveColumnWidths;if(range.rowStart>=range.rowEnd||range.columnStart>=range.columnEnd){return null}const offsets=columnOffsets(columnWidths);const columnStart=clampInteger(range.columnStart,0,columnWidths.length);const columnEnd=clampInteger(range.columnEnd,columnStart,columnWidths.length);const rowStart=clampInteger(range.rowStart,0,layout.rowCount);const rowEnd=clampInteger(range.rowEnd,rowStart,layout.rowCount);if(columnStart===columnEnd||rowStart===rowEnd){return null}const frozenColumns=columnEnd<=layout.frozenColumnCount;const frozenRows=rowEnd<=layout.frozenRowCount;const rowStartOffset=axisPosition(layout.rowGeometry,rowStart);const rowEndOffset=axisPosition(layout.rowGeometry,rowEnd);return Object.freeze({x:layout.rowHeaderWidth+offsets[columnStart]-(frozenColumns?0:layout.horizontal.logicalOffset),y:layout.headerHeight+rowStartOffset-(frozenRows?0:layout.vertical.logicalOffset),width:offsets[columnEnd]-offsets[columnStart],height:rowEndOffset-rowStartOffset})}function columnOffsets(columnWidths){const offsets=new Array(columnWidths.length+1);offsets[0]=0;for(let index=0;index<columnWidths.length;index+=1){offsets[index+1]=offsets[index]+(columnWidths[index]??DEFAULT_COLUMN_WIDTH)}return offsets}function extentValue(extent){return extent.kind==="unknown"?0:extent.value}function createSparseAxisGeometry(count,defaultSize,entries=[]){nonNegativeInteger(count,"axis count");positiveFinite(defaultSize,"axis defaultSize");const byIndex=new Map;for(const entry of entries){if(!entry||!Number.isSafeInteger(entry.index)||entry.index<0||entry.index>=count){continue}if(entry.hidden===true){byIndex.set(entry.index,0);continue}if(entry.size!==void 0){if(!Number.isFinite(entry.size)||entry.size<0){continue}byIndex.set(entry.index,entry.size)}}let cumulativeDelta=0;const overrides=[...byIndex.entries()].sort(([left],[right])=>left-right).map(([index,size])=>{cumulativeDelta+=size-defaultSize;return Object.freeze({index,size,cumulativeDelta})});const contentSize=count*defaultSize+cumulativeDelta;return Object.freeze({count,defaultSize,contentSize:Math.max(0,contentSize),overrides:Object.freeze(overrides)})}function axisPosition(geometry,index){const normalized=clampInteger(index,0,geometry.count);const overrideIndex=lowerBoundOverride(geometry.overrides,normalized);const delta=overrideIndex===0?0:geometry.overrides[overrideIndex-1].cumulativeDelta;return normalized*geometry.defaultSize+delta}function axisSize(geometry,index){if(!Number.isSafeInteger(index)||index<0||index>=geometry.count){return 0}const match=sparseOverrideAt(geometry,index);return match?.size??geometry.defaultSize}function axisIndexAtOffset(geometry,offset){if(!Number.isFinite(offset)||offset<0||offset>=geometry.contentSize||geometry.count===0){return null}let low=0;let high=geometry.count;while(low<high){const middle=Math.floor((low+high)/2);if(axisPosition(geometry,middle+1)<=offset){low=middle+1}else{high=middle}}for(let index=low;index<geometry.count;index+=1){const size=axisSize(geometry,index);const start=axisPosition(geometry,index);if(size>0&&offset>=start&&offset<start+size){return index}if(start>offset){return null}}return null}function nextVisibleAxisIndex(geometry,from,direction=1){for(let index=clampInteger(from,0,Math.max(0,geometry.count-1));index>=0&&index<geometry.count;index+=direction){if(axisSize(geometry,index)>0){return index}}return null}function sparseOverrideAt(geometry,index){const low=lowerBoundOverride(geometry.overrides,index);const candidate=geometry.overrides[low];return candidate?.index===index?candidate:void 0}function lowerBoundOverride(entries,target){let low=0;let high=entries.length;while(low<high){const middle=Math.floor((low+high)/2);if(entries[middle].index<target)low=middle+1;else high=middle}return low}function clampRangeEnd(range,end){return Object.freeze({start:Math.min(range.start,end),end:Math.min(range.end,end)})}function visibleRowRange(geometry,scrollTop,viewportHeight){if(geometry.count===0||viewportHeight<=0||geometry.contentSize<=0){return Object.freeze({start:0,end:0})}const start=axisIndexAtOffset(geometry,scrollTop);if(start===null){return Object.freeze({start:geometry.count,end:geometry.count})}const last=axisIndexAtOffset(geometry,Math.max(scrollTop,Math.min(geometry.contentSize,scrollTop+viewportHeight)-Math.max(1e-7,Math.abs(scrollTop+viewportHeight)*Number.EPSILON*4)));return Object.freeze({start,end:last===null?start+1:Math.min(geometry.count,last+1)})}function visibleColumnRange(offsets,scrollLeft,viewportWidth){const count=Math.max(0,offsets.length-1);if(count===0||viewportWidth<=0){return Object.freeze({start:0,end:0})}const start=columnAtLogicalX(offsets,scrollLeft)??count;const end=Math.min(count,lowerBound(offsets,scrollLeft+viewportWidth));return Object.freeze({start,end:Math.max(start,end)})}function expandRange(range,amount,limit){return Object.freeze({start:Math.max(0,range.start-amount),end:Math.min(limit,range.end+amount)})}function materializeColumns(metadata,widths,offsets,range,frozenRange,rowHeaderWidth,scrollLeft,frozenColumnCount){const indexes=rangeIndexes(range,frozenRange);const columns=[];for(const index of indexes){const column=metadata.schema.columns[index];const width=widths[index]??0;if(width<=0){continue}const frozen=index<frozenColumnCount;columns.push(Object.freeze({index,id:column.id,name:column.name,x:rowHeaderWidth+offsets[index]-(frozen?0:scrollLeft),width,frozen}))}return Object.freeze(columns)}function materializeRows(geometry,range,frozenRange,headerHeight,scrollTop,frozenRowCount){const rows=[];for(const index of rangeIndexes(range,frozenRange)){const height=axisSize(geometry,index);if(height<=0){continue}const frozen=index<frozenRowCount;rows.push(Object.freeze({index,y:headerHeight+axisPosition(geometry,index)-(frozen?0:scrollTop),height,frozen}))}return Object.freeze(rows)}function rangeIndexes(primary,frozen){const indexes=new Set;for(let index=primary.start;index<primary.end;index+=1){indexes.add(index)}for(let index=frozen.start;index<frozen.end;index+=1){indexes.add(index)}return Object.freeze([...indexes].sort((left,right)=>left-right))}function columnAtViewportX(layout,x,columnWidths){if(x<layout.rowHeaderWidth){return null}const bodyX=x-layout.rowHeaderWidth;if(bodyX<Math.min(layout.bodyWidth,layout.frozenColumnExtent)){const frozen=columnAtLogicalX(columnOffsets(columnWidths),bodyX);if(frozen!==null&&frozen<layout.frozenColumnCount){return frozen}}return columnAtLogicalX(columnOffsets(columnWidths),bodyX+layout.horizontal.logicalOffset)}function columnAtLogicalX(offsets,x){const count=Math.max(0,offsets.length-1);if(x<0||count===0||x>=offsets[count]){return null}let low=0;let high=count;while(low<high){const middle=Math.floor((low+high)/2);if(offsets[middle+1]<=x){low=middle+1}else{high=middle}}return low}function rowAtViewportY(layout,y){const bodyY=y-layout.headerHeight;if(bodyY<Math.min(layout.bodyHeight,layout.frozenRowExtent)){const frozen=axisIndexAtOffset(layout.rowGeometry,bodyY);if(frozen!==null&&frozen<layout.frozenRowCount){return frozen}}return axisIndexAtOffset(layout.rowGeometry,bodyY+layout.vertical.logicalOffset)}function lowerBound(values,target){let low=0;let high=values.length;while(low<high){const middle=Math.floor((low+high)/2);if(values[middle]<target){low=middle+1}else{high=middle}}return low}function positiveFinite(value,name){if(!Number.isFinite(value)||value<=0){throw invalidArgument(`${name} must be a positive finite number`)}return value}function nonNegativeFinite(value,name){if(!Number.isFinite(value)||value<0){throw invalidArgument(`${name} must be a non-negative finite number`)}return value}function nonNegativeInteger(value,name){if(!Number.isSafeInteger(value)||value<0){throw invalidArgument(`${name} must be a non-negative safe integer`)}return value}function clampFinite(value,minimum,maximum){if(!Number.isFinite(value)){return minimum}return Math.min(maximum,Math.max(minimum,value))}function clampInteger(value,minimum,maximum){if(!Number.isFinite(value)){return minimum}return Math.min(maximum,Math.max(minimum,Math.trunc(value)))}function createSelection(anchor,focus=anchor){assertCellPosition(anchor,"anchor");assertCellPosition(focus,"focus");const normalizedAnchor=Object.freeze({...anchor});const normalizedFocus=Object.freeze({...focus});return Object.freeze({anchor:normalizedAnchor,focus:normalizedFocus,range:selectionRange(normalizedAnchor,normalizedFocus)})}function selectionRange(anchor,focus){return Object.freeze({rowStart:Math.min(anchor.rowIndex,focus.rowIndex),rowEnd:Math.max(anchor.rowIndex,focus.rowIndex)+1,columnStart:Math.min(anchor.columnIndex,focus.columnIndex),columnEnd:Math.max(anchor.columnIndex,focus.columnIndex)+1})}function containsCell(range,cell){return cell.rowIndex>=range.rowStart&&cell.rowIndex<range.rowEnd&&cell.columnIndex>=range.columnStart&&cell.columnIndex<range.columnEnd}function clampCell(cell,rowCount,columnCount){if(rowCount<=0||columnCount<=0){return null}return Object.freeze({rowIndex:clampInteger2(cell.rowIndex,0,rowCount-1),columnIndex:clampInteger2(cell.columnIndex,0,columnCount-1)})}function moveCell(cell,command,rowCount,columnCount,pageRowCount){const current=clampCell(cell,rowCount,columnCount);if(current===null){return null}const page=Math.max(1,Math.floor(pageRowCount));switch(command){case"left":return clampCell({rowIndex:current.rowIndex,columnIndex:current.columnIndex-1},rowCount,columnCount);case"right":return clampCell({rowIndex:current.rowIndex,columnIndex:current.columnIndex+1},rowCount,columnCount);case"up":return clampCell({rowIndex:current.rowIndex-1,columnIndex:current.columnIndex},rowCount,columnCount);case"down":return clampCell({rowIndex:current.rowIndex+1,columnIndex:current.columnIndex},rowCount,columnCount);case"page-up":return clampCell({rowIndex:current.rowIndex-page,columnIndex:current.columnIndex},rowCount,columnCount);case"page-down":return clampCell({rowIndex:current.rowIndex+page,columnIndex:current.columnIndex},rowCount,columnCount);case"row-start":return Object.freeze({rowIndex:current.rowIndex,columnIndex:0});case"row-end":return Object.freeze({rowIndex:current.rowIndex,columnIndex:columnCount-1});case"table-start":return Object.freeze({rowIndex:0,columnIndex:0});case"table-end":return Object.freeze({rowIndex:rowCount-1,columnIndex:columnCount-1})}}function assertCellPosition(value,name){if(!Number.isSafeInteger(value.rowIndex)||value.rowIndex<0){throw invalidArgument(`${name}.rowIndex must be a non-negative safe integer`)}if(!Number.isSafeInteger(value.columnIndex)||value.columnIndex<0){throw invalidArgument(`${name}.columnIndex must be a non-negative safe integer`)}}function clampInteger2(value,minimum,maximum){if(!Number.isFinite(value)){return minimum}return Math.min(maximum,Math.max(minimum,Math.trunc(value)))}var DEFAULT_MAX_WINDOW_CELLS=1e5;var Controller=class{#table;#options;#listeners=new Set;#unsubscribe;#viewport={width:0,height:0,scrollLeft:0,scrollTop:0,devicePixelRatio:1};#columnWidths;#hiddenColumns;#presentationRows=[];#frozenRows=0;#frozenColumns=0;#mergedCells=[];#snapshot;#activeCell=null;#selection=null;#windows=[];#loadingRanges=[];#requestAbort=null;#generation=0;#loadQueued=false;#disposed=false;#tableClosed=false;constructor(table,options){this.#table=table;this.#options=normalizeOptions(options);this.#columnWidths=initialColumnWidths(table.metadata,this.#options);this.#hiddenColumns=Array.from({length:this.#columnWidths.length},()=>false);const layout=createTableLayout(table.metadata,this.#columnWidths,this.#viewport,this.#layoutOptions());this.#snapshot=Object.freeze({generation:this.#generation,status:"loading",metadata:table.metadata,layout,activeCell:null,selection:null});this.#unsubscribe=table.subscribe(event=>this.#handleTableEvent(event))}get metadata(){return this.#table.metadata}get columnWidths(){return Object.freeze([...this.#columnWidths])}get minColumnWidth(){return this.#options.minColumnWidth}get maxColumnWidth(){return this.#options.maxColumnWidth}getSnapshot(){return this.#snapshot}subscribe(listener){this.#assertOpen();this.#listeners.add(listener);return()=>this.#listeners.delete(listener)}updateViewport(viewport){this.#assertOpen();const normalized=normalizeViewport(viewport);if(sameViewport(normalized,this.#viewport)){return}this.#viewport=normalized;this.#advanceGeneration()}setActiveCell(cell,options={}){this.#assertOpen();assertCellPosition(cell,"cell");const clamped=clampCell(cell,this.#snapshot.layout.rowCount,this.#snapshot.layout.columnCount);if(clamped===null){return}const next=this.#visibleCell(this.#mergeAnchor(clamped),1,1);if(next===null)return;const anchor=options.extendSelection&&this.#selection!==null?this.#selection.anchor:next;this.#activeCell=next;this.#selection=createMergedSelection(anchor,next,this.#mergedCells);if(options.scrollIntoView!==false&&this.#scrollCellIntoView(next)){this.#advanceGeneration();return}this.#publish()}extendSelection(cell,options={}){this.setActiveCell(cell,{...options,extendSelection:true})}setSelection(anchor,focus=anchor){this.#assertOpen();assertCellPosition(anchor,"anchor");assertCellPosition(focus,"focus");const rawAnchor=clampCell(anchor,this.#snapshot.layout.rowCount,this.#snapshot.layout.columnCount);const rawFocus=clampCell(focus,this.#snapshot.layout.rowCount,this.#snapshot.layout.columnCount);if(rawAnchor===null||rawFocus===null){this.clearSelection();return}const clampedAnchor=this.#visibleCell(this.#mergeAnchor(rawAnchor),1,1);const clampedFocus=this.#visibleCell(this.#mergeAnchor(rawFocus),1,1);if(clampedAnchor===null||clampedFocus===null){this.clearSelection();return}this.#activeCell=clampedFocus;this.#selection=createMergedSelection(clampedAnchor,clampedFocus,this.#mergedCells);this.#publish()}clearSelection(){this.#assertOpen();if(this.#selection===null){return}this.#selection=null;this.#publish()}moveActive(command,options={}){this.#assertOpen();const current=this.#activeCell??{rowIndex:0,columnIndex:0};const origin=navigationOrigin(current,command,this.#mergedCells);const moved=moveCell(origin,command,this.#snapshot.layout.rowCount,this.#snapshot.layout.columnCount,Math.max(1,this.#snapshot.layout.visibleRows.filter(row=>!row.frozen).length));const[rowDirection,columnDirection]=navigationDirections(command);const next=moved===null?null:this.#visibleCell(this.#mergeAnchor(moved),rowDirection,columnDirection);if(next!==null){this.setActiveCell(next,options)}}ensureCellVisible(cell){this.#assertOpen();assertCellPosition(cell,"cell");const next=clampCell(cell,this.#snapshot.layout.rowCount,this.#snapshot.layout.columnCount);if(next!==null&&this.#scrollCellIntoView(next)){this.#advanceGeneration()}}ensureColumnVisible(columnIndex){this.#assertOpen();assertColumnIndex(columnIndex,this.#columnWidths.length);if(this.#scrollColumnIntoView(columnIndex)){this.#advanceGeneration()}}resizeColumn(columnIndex,width){this.#assertOpen();assertColumnIndex(columnIndex,this.#columnWidths.length);const normalized=clampWidth(width,this.#options);if(this.#columnWidths[columnIndex]===normalized){return}this.#columnWidths[columnIndex]=normalized;this.#advanceGeneration()}autosizeColumn(columnIndex,measuredWidth){this.resizeColumn(columnIndex,measuredWidth)}applySpreadsheetPresentation(presentation){this.#assertOpen();if(presentation.kind!=="spreadsheet-v1"||presentation.tableId!==this.metadata.tableId){throw invalidArgument("presentation must belong to the controller table")}const nextWidths=[...this.#columnWidths];const hiddenColumns=Array.from({length:nextWidths.length},()=>false);for(const entry of presentation.columns){if(!Number.isSafeInteger(entry.index)||entry.index<0||entry.index>=nextWidths.length){continue}hiddenColumns[entry.index]=entry.hidden===true;if(entry.size!==void 0&&Number.isFinite(entry.size)&&entry.size>0){nextWidths[entry.index]=clampWidth(entry.size,this.#options)}}this.#columnWidths=nextWidths;this.#hiddenColumns=hiddenColumns;this.#presentationRows=Object.freeze(presentation.rows.map(entry=>Object.freeze({...entry})));this.#frozenRows=presentation.frozenRows;this.#frozenColumns=presentation.frozenColumns;this.#mergedCells=[];this.#advanceGeneration();this.#normalizeInteractionForPresentation();this.#publish()}setMergedCells(regions){this.#assertOpen();const normalized=normalizeMergedCells(regions,this.#snapshot.layout.rowCount,this.#snapshot.layout.columnCount);if(sameMergedCells(normalized,this.#mergedCells)){return}this.#mergedCells=normalized;this.#normalizeInteractionForPresentation();this.#advanceGeneration()}hitTest(x,y,resizeHandleWidth){const result=hitTest(this.#snapshot.layout,x,y,this.#columnWidths,resizeHandleWidth);if(result.kind!=="cell"){return result}const anchor=this.#mergeAnchor(result);return Object.freeze({kind:"cell",...anchor})}cellRect(cell){assertCellPosition(cell,"cell");if(cell.rowIndex>=this.#snapshot.layout.rowCount||cell.columnIndex>=this.#snapshot.layout.columnCount){throw invalidArgument("cell must be inside the current table extent")}return cellRect(this.#snapshot.layout,this.#columnWidths,cell.rowIndex,cell.columnIndex)}selectionRect(selection=this.#selection){return selection===null?null:selectionRect(this.#snapshot.layout,this.#columnWidths,selection.range)}getCell(rowIndex,columnIndex){if(!isValidCell(rowIndex,columnIndex,this.#snapshot.layout.rowCount,this.#columnWidths.length)){return Object.freeze({status:"unavailable"})}for(const window of this.#windows){if(rangeContains(window.range,rowIndex,columnIndex)){return Object.freeze({status:"loaded",value:window.rows[rowIndex-window.range.rowStart][columnIndex-window.range.columnStart]})}}for(const range of this.#loadingRanges){if(rangeContains(range,rowIndex,columnIndex)){return Object.freeze({status:"loading"})}}return Object.freeze({status:"unavailable"})}async copySelection(options={}){this.#assertOpen();const selection=this.#selection;if(selection===null){return""}const request=gridRangeToRequest(selection.range);assertWindowCellLimit(request,MAX_RANGE_CELLS,"selection");const batch=await this.#table.readRange(request,options.signal?{signal:options.signal}:{});return rowsToTsv(batchRows(batch))}dispose(){if(this.#disposed){return}this.#disposed=true;this.#requestAbort?.abort();this.#requestAbort=null;this.#unsubscribe();this.#listeners.clear();this.#windows=[];this.#loadingRanges=[];this.#snapshot=Object.freeze({...this.#snapshot,status:"closed",activeCell:null,selection:null})}#advanceGeneration(){this.#generation+=1;this.#requestAbort?.abort();this.#requestAbort=null;this.#loadingRanges=[];this.#rebuildSnapshot("loading");this.#queueLoad()}#queueLoad(){if(this.#loadQueued||this.#disposed){return}this.#loadQueued=true;queueMicrotask(()=>{this.#loadQueued=false;if(!this.#disposed){void this.#loadGeneration(this.#generation)}})}async#loadGeneration(generation){let visible;let overscan;try{({visible,overscan}=layoutRanges(this.#snapshot.layout,this.#options.maxWindowCells,this.#mergedCells))}catch(error){if(!this.#disposed&&generation===this.#generation){this.#loadingRanges=[];this.#rebuildSnapshot("error",error)}return}if(visible.length===0){this.#windows=[];this.#loadingRanges=[];this.#rebuildSnapshot("ready");return}const abort=new AbortController;this.#requestAbort=abort;const ranges=Object.freeze([...visible,...overscan.filter(candidate=>!visible.some(required=>rangeContainsRange(candidate,required)&&sameGridRange(candidate,required)))]);this.#loadingRanges=Object.freeze(ranges);this.#publish();let loaded=[];try{for(let index=0;index<ranges.length;index+=1){const range=ranges[index];const batch=await this.#table.readRange(gridRangeToRequest(range),{signal:abort.signal});if(this.#disposed||generation!==this.#generation||abort.signal.aborted){return}const returnedRange=batchGridRange(batch);if(returnedRange.rowStart<returnedRange.rowEnd&&returnedRange.columnStart<returnedRange.columnEnd){const window=Object.freeze({range:returnedRange,rows:batchRows(batch)});loaded=[window,...loaded.filter(candidate=>!rangeContainsRange(returnedRange,candidate.range))]}this.#windows=Object.freeze([...loaded]);this.#loadingRanges=Object.freeze(ranges.slice(index+1));this.#rebuildSnapshot("ready")}}catch(error){if(this.#disposed||generation!==this.#generation||abort.signal.aborted||error instanceof TabularkError&&error.code==="CANCELLED"){return}this.#loadingRanges=[];this.#rebuildSnapshot("error",error)}finally{if(this.#requestAbort===abort){this.#requestAbort=null}}}#scrollCellIntoView(cell){const layout=this.#snapshot.layout;const offsets=columnOffsets(layout.effectiveColumnWidths);const cellLeft=offsets[cell.columnIndex];const cellRight=offsets[cell.columnIndex+1];const cellTop=axisPosition(layout.rowGeometry,cell.rowIndex);const cellBottom=cellTop+axisSize(layout.rowGeometry,cell.rowIndex);const logicalLeft=cell.columnIndex<layout.frozenColumnCount?layout.horizontal.logicalOffset:revealOffset(layout.horizontal.logicalOffset,layout.bodyWidth,cellLeft,cellRight,layout.horizontal.logicalMaxOffset);const logicalTop=cell.rowIndex<layout.frozenRowCount?layout.vertical.logicalOffset:revealOffset(layout.vertical.logicalOffset,layout.bodyHeight,cellTop,cellBottom,layout.vertical.logicalMaxOffset);const scrollLeft=logicalToPhysicalOffset(layout.horizontal,logicalLeft);const scrollTop=logicalToPhysicalOffset(layout.vertical,logicalTop);if(scrollLeft===this.#viewport.scrollLeft&&scrollTop===this.#viewport.scrollTop){return false}this.#viewport=Object.freeze({...this.#viewport,scrollLeft,scrollTop});return true}#scrollColumnIntoView(columnIndex){const layout=this.#snapshot.layout;const offsets=columnOffsets(layout.effectiveColumnWidths);const logicalLeft=columnIndex<layout.frozenColumnCount?layout.horizontal.logicalOffset:revealOffset(layout.horizontal.logicalOffset,layout.bodyWidth,offsets[columnIndex],offsets[columnIndex+1],layout.horizontal.logicalMaxOffset);const scrollLeft=logicalToPhysicalOffset(layout.horizontal,logicalLeft);if(scrollLeft===this.#viewport.scrollLeft){return false}this.#viewport=Object.freeze({...this.#viewport,scrollLeft});return true}#handleTableEvent(event){if(this.#disposed){return}switch(event.type){case"metadata":{const previousMetadata=this.#snapshot.metadata;this.#columnWidths=reconcileColumnWidths(previousMetadata,event.metadata,this.#columnWidths,this.#options);const hiddenById=new Map(previousMetadata.schema.columns.map((column,index)=>[column.id,this.#hiddenColumns[index]===true]));this.#hiddenColumns=event.metadata.schema.columns.map(column=>hiddenById.get(column.id)===true);this.#advanceGeneration();this.#clampInteraction();this.#publish();break}case"runtimeError":this.#requestAbort?.abort();this.#requestAbort=null;this.#loadingRanges=[];this.#rebuildSnapshot("error",event.error);break;case"closed":this.#tableClosed=true;this.#requestAbort?.abort();this.#requestAbort=null;this.#loadingRanges=[];if(this.#snapshot.status!=="error"){this.#rebuildSnapshot("closed")}break;case"warning":break}}#clampInteraction(){if(this.#activeCell===null){return}const rowCount=extentValue(this.#table.metadata.extent.rows);const columnCount=this.#table.metadata.schema.columns.length;const rawActive=clampCell(this.#activeCell,rowCount,columnCount);const nextActive=rawActive===null?null:this.#visibleCell(this.#mergeAnchor(rawActive),1,1);if(nextActive===null){this.#activeCell=null;this.#selection=null;return}this.#activeCell=nextActive;if(this.#selection!==null){const rawAnchor=clampCell(this.#selection.anchor,rowCount,columnCount);const rawFocus=clampCell(this.#selection.focus,rowCount,columnCount);const anchor=this.#visibleCell(this.#mergeAnchor(rawAnchor),1,1);const focus=this.#visibleCell(this.#mergeAnchor(rawFocus),1,1);this.#selection=anchor===null||focus===null?null:createMergedSelection(anchor,focus,this.#mergedCells)}}#layoutOptions(){return Object.freeze({...this.#options,rowEntries:this.#presentationRows,hiddenColumns:this.#hiddenColumns,frozenRows:this.#frozenRows,frozenColumns:this.#frozenColumns})}#mergeAnchor(cell){const region=mergeContaining(this.#mergedCells,cell);return region===void 0?Object.freeze({rowIndex:cell.rowIndex,columnIndex:cell.columnIndex}):Object.freeze({rowIndex:region.rowStart,columnIndex:region.columnStart})}#visibleCell(cell,rowDirection,columnDirection){const layout=this.#snapshot.layout;const row=nextVisibleAxisIndex(layout.rowGeometry,cell.rowIndex,rowDirection)??nextVisibleAxisIndex(layout.rowGeometry,cell.rowIndex,rowDirection===1?-1:1);if(row===null)return null;const column=nextVisibleColumn(layout.effectiveColumnWidths,cell.columnIndex,columnDirection)??nextVisibleColumn(layout.effectiveColumnWidths,cell.columnIndex,columnDirection===1?-1:1);return column===null?null:Object.freeze({rowIndex:row,columnIndex:column})}#normalizeInteractionForPresentation(){this.#clampInteraction()}#rebuildSnapshot(status,error){const layout=createTableLayout(this.#table.metadata,this.#columnWidths,this.#viewport,this.#layoutOptions());this.#viewport=Object.freeze({...this.#viewport,scrollLeft:layout.horizontal.physicalOffset,scrollTop:layout.vertical.physicalOffset});this.#snapshot=Object.freeze({generation:this.#generation,status,metadata:this.#table.metadata,layout,activeCell:this.#activeCell,selection:this.#selection,...error===void 0?{}:{error}});this.#emit()}#publish(){this.#snapshot=Object.freeze({...this.#snapshot,activeCell:this.#activeCell,selection:this.#selection});this.#emit()}#emit(){for(const listener of this.#listeners){try{listener(this.#snapshot)}catch(error){if(typeof reportError==="function"){reportError(error)}}}}#assertOpen(){if(this.#disposed||this.#tableClosed||this.#snapshot.status==="closed"){throw closedError("Table view controller")}}};function createTableController(table,options={}){if(!table||typeof table.readRange!=="function"||typeof table.subscribe!=="function"){throw invalidArgument("table must be a TableHandle")}return new Controller(table,options)}function normalizeMergedCells(regions,rowCount,columnCount){const unique=new Map;for(const region of regions){if(!region||!Number.isSafeInteger(region.rowStart)||!Number.isSafeInteger(region.rowEnd)||!Number.isSafeInteger(region.columnStart)||!Number.isSafeInteger(region.columnEnd)||region.rowStart<0||region.columnStart<0||region.rowEnd<=region.rowStart||region.columnEnd<=region.columnStart||region.rowEnd>rowCount||region.columnEnd>columnCount){continue}const frozen=Object.freeze({...region});unique.set(mergeKey(frozen),frozen)}return Object.freeze([...unique.values()].sort((left,right)=>left.rowStart-right.rowStart||left.columnStart-right.columnStart||left.rowEnd-right.rowEnd||left.columnEnd-right.columnEnd))}function sameMergedCells(left,right){return left.length===right.length&&left.every((region,index)=>mergeKey(region)===mergeKey(right[index]))}function mergeKey(region){return`${region.rowStart}:${region.rowEnd}:${region.columnStart}:${region.columnEnd}`}function mergeContaining(regions,cell){return regions.find(region=>cell.rowIndex>=region.rowStart&&cell.rowIndex<region.rowEnd&&cell.columnIndex>=region.columnStart&&cell.columnIndex<region.columnEnd)}function createMergedSelection(anchor,focus,regions){const selection=createSelection(anchor,focus);let range=selection.range;for(;;){let expanded=range;for(const region of regions){if(!rangesIntersect(expanded,region))continue;expanded=Object.freeze({rowStart:Math.min(expanded.rowStart,region.rowStart),rowEnd:Math.max(expanded.rowEnd,region.rowEnd),columnStart:Math.min(expanded.columnStart,region.columnStart),columnEnd:Math.max(expanded.columnEnd,region.columnEnd)})}if(sameGridRange(range,expanded))break;range=expanded}return Object.freeze({...selection,range})}function rangesIntersect(left,right){return left.rowStart<right.rowEnd&&left.rowEnd>right.rowStart&&left.columnStart<right.columnEnd&&left.columnEnd>right.columnStart}function navigationOrigin(current,command,regions){const merge=mergeContaining(regions,current);if(merge===void 0)return current;if(command==="right"){return Object.freeze({rowIndex:current.rowIndex,columnIndex:merge.columnEnd-1})}if(command==="down"||command==="page-down"){return Object.freeze({rowIndex:merge.rowEnd-1,columnIndex:current.columnIndex})}return current}function navigationDirections(command){switch(command){case"left":case"row-start":return[1,-1];case"up":case"page-up":return[-1,1];case"table-start":return[-1,-1];default:return[1,1]}}function nextVisibleColumn(widths,from,direction){for(let index=Math.min(widths.length-1,Math.max(0,from));index>=0&&index<widths.length;index+=direction){if((widths[index]??0)>0)return index}return null}function normalizeOptions(options){const minColumnWidth=positiveFinite2(options.minColumnWidth??DEFAULT_MIN_COLUMN_WIDTH,"minColumnWidth");const maxColumnWidth=positiveFinite2(options.maxColumnWidth??DEFAULT_MAX_COLUMN_WIDTH,"maxColumnWidth");if(maxColumnWidth<minColumnWidth){throw invalidArgument("maxColumnWidth must be greater than or equal to minColumnWidth")}const maxWindowCells=positiveInteger(options.maxWindowCells??DEFAULT_MAX_WINDOW_CELLS,"maxWindowCells");if(maxWindowCells>MAX_RANGE_CELLS){throw invalidArgument(`maxWindowCells cannot exceed ${MAX_RANGE_CELLS}`)}return Object.freeze({...options.rowHeight===void 0?{}:{rowHeight:options.rowHeight},...options.headerHeight===void 0?{}:{headerHeight:options.headerHeight},...options.rowHeaderWidth===void 0?{}:{rowHeaderWidth:options.rowHeaderWidth},...options.scrollPixelLimit===void 0?{}:{scrollPixelLimit:options.scrollPixelLimit},...options.overscanRows===void 0?{}:{overscanRows:options.overscanRows},...options.overscanColumns===void 0?{}:{overscanColumns:options.overscanColumns},columnWidth:positiveFinite2(options.columnWidth??DEFAULT_COLUMN_WIDTH,"columnWidth"),minColumnWidth,maxColumnWidth,columnWidths:Object.freeze({...options.columnWidths??{}}),maxWindowCells})}function initialColumnWidths(metadata,options){return metadata.schema.columns.map(column=>clampWidth(options.columnWidths[column.id]??options.columnWidth,options))}function reconcileColumnWidths(previousMetadata,nextMetadata,previousWidths,options){const byId=new Map(previousMetadata.schema.columns.map((column,index)=>[column.id,previousWidths[index]]));return nextMetadata.schema.columns.map(column=>clampWidth(byId.get(column.id)??options.columnWidths[column.id]??options.columnWidth,options))}function normalizeViewport(viewport){return Object.freeze({width:nonNegativeFinite2(viewport.width,"viewport.width"),height:nonNegativeFinite2(viewport.height,"viewport.height"),scrollLeft:nonNegativeFinite2(viewport.scrollLeft,"viewport.scrollLeft"),scrollTop:nonNegativeFinite2(viewport.scrollTop,"viewport.scrollTop"),devicePixelRatio:positiveFinite2(viewport.devicePixelRatio??1,"viewport.devicePixelRatio")})}function layoutRanges(layout,maxCells,mergedCells=[]){const visible=windowRectangles(layout.rows.visible,layout.columns.visible,layout.frozenRowRange,layout.frozenColumnRange);const anchors=mergeAnchorRanges(mergedCells,visible);const visibleWithAnchors=Object.freeze([...visible,...anchors.filter(anchor=>!visible.some(range=>rangeContainsRange(range,anchor)))]);const visibleCellCount=visibleWithAnchors.reduce((total,range)=>total+rangeCellCount(range),0);if(visibleCellCount>maxCells){throw new TabularkError("RESOURCE_LIMIT",`The viewport contains ${visibleCellCount} cells; the window limit is ${maxCells}`,{details:{resource:"viewport-cells",required:visibleCellCount,available:maxCells,visibleCellCount,maxCells}})}const fullOverscan=windowRectangles(layout.rows.overscan,layout.columns.overscan,layout.frozenRowRange,layout.frozenColumnRange);const overscanCellCount=fullOverscan.reduce((total,range)=>total+rangeCellCount(range),0);const overscanWithAnchors=Object.freeze([...fullOverscan,...anchors.filter(anchor=>!fullOverscan.some(range=>rangeContainsRange(range,anchor)))]);return Object.freeze({visible:visibleWithAnchors,overscan:overscanCellCount+anchors.length<=maxCells?overscanWithAnchors:visibleWithAnchors})}function mergeAnchorRanges(mergedCells,visible){const anchors=new Map;for(const region of mergedCells){if(!visible.some(range2=>rangesIntersect(range2,region)))continue;const range=Object.freeze({rowStart:region.rowStart,rowEnd:region.rowStart+1,columnStart:region.columnStart,columnEnd:region.columnStart+1});anchors.set(`${range.rowStart}:${range.columnStart}`,range)}return Object.freeze([...anchors.values()])}function windowRectangles(rows,columns,frozenRows,frozenColumns){const rowBands=mergeIndexBands(rows,frozenRows);const columnBands=mergeIndexBands(columns,frozenColumns);const rectangles=[];for(const row of rowBands){for(const column of columnBands){rectangles.push(Object.freeze({rowStart:row.start,rowEnd:row.end,columnStart:column.start,columnEnd:column.end}))}}return Object.freeze(rectangles)}function mergeIndexBands(primary,frozen){const bands=[primary,frozen].filter(range=>range.start<range.end).sort((left,right)=>left.start-right.start);if(bands.length<=1)return Object.freeze(bands.map(range=>Object.freeze({...range})));const[first,second]=bands;return first.end>=second.start?Object.freeze([Object.freeze({start:first.start,end:Math.max(first.end,second.end)})]):Object.freeze(bands.map(range=>Object.freeze({...range})))}function gridRangeToRequest(range){return Object.freeze({rowStart:range.rowStart,rowCount:range.rowEnd-range.rowStart,columnStart:range.columnStart,columnCount:range.columnEnd-range.columnStart})}function batchGridRange(batch){return Object.freeze({rowStart:batch.range.rowStart,rowEnd:batch.range.rowStart+batch.range.rowCount,columnStart:batch.range.columnStart,columnEnd:batch.range.columnStart+batch.range.columnCount})}function batchRows(batch){return Object.freeze(batch.toDisplayRows({maxCells:Math.max(1,batch.range.rowCount*batch.range.columnCount)}).map(row=>Object.freeze(row)))}function rowsToTsv(rows){return rows.map(row=>row.map(tsvField).join(" ")).join("\n")}function tsvField(value){if(value===null){return""}return/[\t\r\n"]/.test(value)?`"${value.replaceAll('"','""')}"`:value}function revealOffset(current,viewportSize,itemStart,itemEnd,maximum){if(viewportSize<=0||itemStart<current){return Math.min(maximum,Math.max(0,itemStart))}if(itemEnd>current+viewportSize){return Math.min(maximum,Math.max(0,itemEnd-viewportSize))}return current}function rangeContains(range,rowIndex,columnIndex){return rowIndex>=range.rowStart&&rowIndex<range.rowEnd&&columnIndex>=range.columnStart&&columnIndex<range.columnEnd}function rangeContainsRange(container,candidate){return candidate.rowStart>=container.rowStart&&candidate.rowEnd<=container.rowEnd&&candidate.columnStart>=container.columnStart&&candidate.columnEnd<=container.columnEnd}function rangeCellCount(range){return(range.rowEnd-range.rowStart)*(range.columnEnd-range.columnStart)}function sameGridRange(left,right){return right!==null&&left.rowStart===right.rowStart&&left.rowEnd===right.rowEnd&&left.columnStart===right.columnStart&&left.columnEnd===right.columnEnd}function sameViewport(left,right){return left.width===right.width&&left.height===right.height&&left.scrollLeft===right.scrollLeft&&left.scrollTop===right.scrollTop&&left.devicePixelRatio===right.devicePixelRatio}function isValidCell(rowIndex,columnIndex,rowCount,columnCount){return Number.isSafeInteger(rowIndex)&&rowIndex>=0&&rowIndex<rowCount&&Number.isSafeInteger(columnIndex)&&columnIndex>=0&&columnIndex<columnCount}function assertColumnIndex(columnIndex,columnCount){if(!Number.isSafeInteger(columnIndex)||columnIndex<0||columnIndex>=columnCount){throw invalidArgument(`columnIndex must be between 0 and ${Math.max(0,columnCount-1)}`)}}function assertWindowCellLimit(request,limit,label){const cells=request.rowCount*request.columnCount;if(!Number.isSafeInteger(cells)||cells>limit){throw new TabularkError("RESOURCE_LIMIT",`The ${label} contains ${cells} cells; the limit is ${limit}`,{details:{resource:"view-window-cells",required:cells,available:limit,cells,limit}})}}function clampWidth(width,options){if(!Number.isFinite(width)){throw invalidArgument("column width must be a finite number")}return Math.min(options.maxColumnWidth,Math.max(options.minColumnWidth,width))}function positiveFinite2(value,name){if(!Number.isFinite(value)||value<=0){throw invalidArgument(`${name} must be a positive finite number`)}return value}function nonNegativeFinite2(value,name){if(!Number.isFinite(value)||value<0){throw invalidArgument(`${name} must be a non-negative finite number`)}return value}function positiveInteger(value,name){if(!Number.isSafeInteger(value)||value<=0){throw invalidArgument(`${name} must be a positive safe integer`)}return value}export{CanvasTablePainter,DEFAULT_CANVAS_TABLE_THEME,DEFAULT_COLUMN_WIDTH,DEFAULT_HEADER_HEIGHT,DEFAULT_MAX_COLUMN_WIDTH,DEFAULT_MIN_COLUMN_WIDTH,DEFAULT_OVERSCAN_COLUMNS,DEFAULT_OVERSCAN_ROWS,DEFAULT_ROW_HEADER_WIDTH,DEFAULT_ROW_HEIGHT,DEFAULT_SCROLL_PIXEL_LIMIT,axisIndexAtOffset,axisPosition,axisSize,cellRect,clampCell,columnHeaderRect,containsCell,createScrollAxis,createSelection,createSparseAxisGeometry,createTableController,createTableLayout,hitTest,logicalToPhysicalOffset,moveCell,nextVisibleAxisIndex,physicalToLogicalOffset,rowHeaderRect,selectionRange,selectionRect};
1
+ var DEFAULT_CANVAS_TABLE_THEME=Object.freeze({background:"#ffffff",foreground:"#172033",mutedForeground:"#667085",headerBackground:"#f2f4f7",headerForeground:"#344054",alternateRowBackground:"#f9fafb",gridLine:"#d0d5dd",selectionBackground:"rgba(21, 112, 239, 0.14)",selectionBorder:"#1570ef",activeCellBorder:"#175cd3",loadingBackground:"#e4e7ec",font:'13px ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif',headerFont:'600 13px ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif'});var CanvasTablePainter=class{canvas;#context;#theme;#maxDevicePixelRatio;#forcedColors;#cssWidth=0;#cssHeight=0;#devicePixelRatio=1;constructor(canvas,options={}){const context=canvas.getContext("2d",{alpha:false});if(context===null){throw new Error("Tabulark Canvas rendering requires a 2D rendering context")}this.canvas=canvas;this.#context=context;this.#theme=Object.freeze({...DEFAULT_CANVAS_TABLE_THEME,...options.theme});this.#maxDevicePixelRatio=positive(options.maxDevicePixelRatio,Number.POSITIVE_INFINITY);this.#forcedColors=options.forcedColors??false}setTheme(theme,options={}){this.#theme=Object.freeze({...theme});this.#forcedColors=options.forcedColors??false}paint(snapshot){this.#resize(snapshot.width,snapshot.height);const context=this.#context;const theme=this.#theme;context.save();context.setTransform(this.#devicePixelRatio,0,0,this.#devicePixelRatio,0,0);context.clearRect(0,0,snapshot.width,snapshot.height);context.fillStyle=theme.background;context.fillRect(0,0,snapshot.width,snapshot.height);this.#paintRows(snapshot);this.#paintHeader(snapshot);this.#paintGrid(snapshot);this.#paintMergedCells(snapshot);this.#paintSelection(snapshot);context.restore()}#resize(width,height){const view=this.canvas.ownerDocument.defaultView;const browserRatio=view?.devicePixelRatio??1;const ratio=Math.max(1,Math.min(browserRatio,this.#maxDevicePixelRatio));const cssWidth=Math.max(0,Math.floor(width));const cssHeight=Math.max(0,Math.floor(height));const pixelWidth=Math.max(1,Math.round(cssWidth*ratio));const pixelHeight=Math.max(1,Math.round(cssHeight*ratio));if(cssWidth===this.#cssWidth&&cssHeight===this.#cssHeight&&ratio===this.#devicePixelRatio&&this.canvas.width===pixelWidth&&this.canvas.height===pixelHeight){return}this.#cssWidth=cssWidth;this.#cssHeight=cssHeight;this.#devicePixelRatio=ratio;this.canvas.width=pixelWidth;this.canvas.height=pixelHeight;this.canvas.style.width=`${cssWidth}px`;this.canvas.style.height=`${cssHeight}px`}#paintRows(snapshot){const context=this.#context;const theme=this.#theme;const columnsByIndex=new Map(snapshot.columns.map(column=>[column.index,column]));context.font=theme.font;context.textBaseline="middle";const rows=[...snapshot.rows.filter(row=>row.frozen!==true),...snapshot.rows.filter(row=>row.frozen===true)];for(const row of rows){if(row.y+row.height<=snapshot.headerHeight||row.y>=snapshot.height){continue}context.fillStyle=row.index%2===0?theme.background:theme.alternateRowBackground;context.fillRect(0,row.y,snapshot.width,row.height);context.fillStyle=theme.mutedForeground;context.textAlign="right";context.fillText(String(row.index+1),snapshot.rowGutterWidth-10,row.y+row.height/2);const cells=[...row.cells.filter(cell=>columnsByIndex.get(cell.columnIndex)?.frozen!==true),...row.cells.filter(cell=>columnsByIndex.get(cell.columnIndex)?.frozen===true)];for(const cell of cells){const column=columnsByIndex.get(cell.columnIndex);if(column===void 0||column.x+column.width<=snapshot.rowGutterWidth||cell.coveredByMerge===true){continue}this.#paintCellValue(cell.value,column.x,row.y,column.width,row.height,cell.style)}}}#paintCellValue(value,x,y,width,height,style){const context=this.#context;const theme=this.#theme;const horizontalPadding=10;if(width<=horizontalPadding*2){return}context.save();context.beginPath();context.rect(x+1,y+1,Math.max(0,width-2),Math.max(0,height-2));context.clip();this.#paintCellFill(x,y,width,height,style);this.#applyCellFont(style);context.textAlign=horizontalAlignment(style);context.textBaseline=verticalBaseline(style);if(value===void 0){context.fillStyle=theme.loadingBackground;const skeletonWidth=Math.max(12,Math.min(width-horizontalPadding*2,width*.42));context.fillRect(x+horizontalPadding,y+height/2-3,skeletonWidth,6)}else{context.fillStyle=value===null?theme.mutedForeground:this.#cellForeground(style,theme.foreground);const label=value===null?"\u2205":value;const textX=horizontalTextX(context.textAlign,x,width,horizontalPadding);const textY=verticalTextY(context.textBaseline,y,height,4);context.fillText(label,textX,textY,Math.max(0,width-horizontalPadding*2));if(style?.font?.underline===true){const measured=Math.min(context.measureText(label).width,width-horizontalPadding*2);const start=underlineStart(context.textAlign,textX,measured);context.strokeStyle=String(context.fillStyle);context.lineWidth=1;context.beginPath();context.moveTo(start,Math.min(y+height-4,textY+3));context.lineTo(start+measured,Math.min(y+height-4,textY+3));context.stroke()}}this.#paintCellBorders(x,y,width,height,style);context.restore()}#paintMergedCells(snapshot){for(const merged of snapshot.mergedCells??[]){if(merged.width<=0||merged.height<=0||merged.x+merged.width<=snapshot.rowGutterWidth||merged.y+merged.height<=snapshot.headerHeight||merged.x>=snapshot.width||merged.y>=snapshot.height){continue}this.#context.fillStyle=merged.region.rowStart%2===0?this.#theme.background:this.#theme.alternateRowBackground;this.#context.fillRect(merged.x,merged.y,merged.width,merged.height);if(merged.continuation===true){this.#paintCellFill(merged.x,merged.y,merged.width,merged.height,merged.style);this.#paintCellBorders(merged.x,merged.y,merged.width,merged.height,merged.style)}else{this.#paintCellValue(merged.value,merged.x,merged.y,merged.width,merged.height,merged.style)}this.#context.strokeStyle=this.#theme.gridLine;this.#context.lineWidth=1/this.#devicePixelRatio;this.#context.strokeRect(merged.x+.5/this.#devicePixelRatio,merged.y+.5/this.#devicePixelRatio,Math.max(0,merged.width-1/this.#devicePixelRatio),Math.max(0,merged.height-1/this.#devicePixelRatio))}}#paintCellFill(x,y,width,height,style){if(this.#forcedColors||style===void 0)return;const color=cssColor(style.fillColor??style.backgroundColor);if(color===void 0)return;this.#context.fillStyle=color;this.#context.fillRect(x+1,y+1,Math.max(0,width-2),Math.max(0,height-2))}#applyCellFont(style){if(style?.font===void 0){this.#context.font=this.#theme.font;return}const font=style.font;const size=finitePositive(font.size)??13;const family=font.family&&font.family.trim().length>0?font.family:'ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif';this.#context.font=`${font.italic===true?"italic ":""}${font.bold===true?"700 ":"400 "}${size}px ${family}`}#cellForeground(style,fallback){if(this.#forcedColors)return fallback;return cssColor(style?.font?.color)??cssColor(style?.foregroundColor)??fallback}#paintCellBorders(x,y,width,height,style){if(style?.borders===void 0)return;const context=this.#context;const sides=style.borders;const paint=(side,startX,startY,endX,endY)=>{const border=sides[side];if(border===void 0||border.style==="none")return;context.save();const widthForStyle=borderWidth(border.style);context.lineWidth=widthForStyle;context.strokeStyle=this.#forcedColors?this.#theme.gridLine:cssColor(border.color)??this.#theme.gridLine;context.setLineDash(borderDash(border.style));context.beginPath();context.moveTo(startX,startY);context.lineTo(endX,endY);context.stroke();if(border.style==="double"){const offset=Math.max(2,widthForStyle+1);const horizontal=startY===endY;context.beginPath();context.moveTo(horizontal?startX:startX+offset,horizontal?startY+offset:startY);context.lineTo(horizontal?endX:endX+offset,horizontal?endY+offset:endY);context.stroke()}context.restore()};paint("top",x+.5,y+.5,x+width-.5,y+.5);paint("right",x+width-.5,y+.5,x+width-.5,y+height-.5);paint("bottom",x+.5,y+height-.5,x+width-.5,y+height-.5);paint("left",x+.5,y+.5,x+.5,y+height-.5)}#paintHeader(snapshot){const context=this.#context;const theme=this.#theme;context.fillStyle=theme.headerBackground;context.fillRect(0,0,snapshot.width,snapshot.headerHeight);context.font=theme.headerFont;context.fillStyle=theme.headerForeground;context.textBaseline="middle";context.textAlign="left";const columns=[...snapshot.columns.filter(column=>column.frozen!==true),...snapshot.columns.filter(column=>column.frozen===true)];for(const column of columns){if(column.x+column.width<=snapshot.rowGutterWidth||column.x>=snapshot.width){continue}context.save();context.beginPath();context.rect(column.x+1,0,Math.max(0,column.width-2),snapshot.headerHeight);context.clip();context.fillText(column.name,column.x+10,snapshot.headerHeight/2);context.restore()}context.textAlign="center";context.fillText("#",snapshot.rowGutterWidth/2,snapshot.headerHeight/2,snapshot.rowGutterWidth)}#paintGrid(snapshot){const context=this.#context;const theme=this.#theme;context.strokeStyle=theme.gridLine;context.lineWidth=1/this.#devicePixelRatio;context.beginPath();const snap=.5/this.#devicePixelRatio;context.moveTo(snapshot.rowGutterWidth+snap,0);context.lineTo(snapshot.rowGutterWidth+snap,snapshot.height);context.moveTo(0,snapshot.headerHeight+snap);context.lineTo(snapshot.width,snapshot.headerHeight+snap);for(const column of snapshot.columns){const x=column.x+column.width+snap;if(x>snapshot.rowGutterWidth&&x<snapshot.width){context.moveTo(x,0);context.lineTo(x,snapshot.height)}}for(const row of snapshot.rows){const y=row.y+row.height+snap;if(y>snapshot.headerHeight&&y<snapshot.height){context.moveTo(0,y);context.lineTo(snapshot.width,y)}}context.stroke()}#paintSelection(snapshot){const selection=snapshot.selection;const context=this.#context;const theme=this.#theme;if(selection!==void 0){const selectedColumns=snapshot.columns.filter(column=>column.index>=selection.columnStart&&column.index<selection.columnEnd);const selectedRows=snapshot.rows.filter(row=>row.index>=selection.rowStart&&row.index<selection.rowEnd);if(selectedColumns.length>0&&selectedRows.length>0){const firstColumn=selectedColumns[0];const lastColumn=selectedColumns.at(-1);const firstRow=selectedRows[0];const lastRow=selectedRows.at(-1);const x=Math.max(snapshot.rowGutterWidth,firstColumn.x);const y=Math.max(snapshot.headerHeight,firstRow.y);const right=Math.min(snapshot.width,lastColumn.x+lastColumn.width);const bottom=Math.min(snapshot.height,lastRow.y+lastRow.height);if(right>x&&bottom>y){context.save();context.fillStyle=theme.selectionBackground;if(this.#forcedColors){context.globalAlpha=.28}context.fillRect(x,y,right-x,bottom-y);context.restore();context.strokeStyle=theme.selectionBorder;context.lineWidth=this.#forcedColors?2:1;context.setLineDash(this.#forcedColors?[4,2]:[]);context.strokeRect(x+context.lineWidth/2,y+context.lineWidth/2,Math.max(0,right-x-context.lineWidth),Math.max(0,bottom-y-context.lineWidth));context.setLineDash([])}}}const activeMerges=snapshot.activeCell===void 0?[]:snapshot.mergedCells?.filter(merged=>snapshot.activeCell.rowIndex>=merged.region.rowStart&&snapshot.activeCell.rowIndex<merged.region.rowEnd&&snapshot.activeCell.columnIndex>=merged.region.columnStart&&snapshot.activeCell.columnIndex<merged.region.columnEnd)??[];const activeColumn=snapshot.activeCell===void 0?void 0:snapshot.columns.find(column=>column.index===snapshot.activeCell.columnIndex);const activeRow=snapshot.activeCell===void 0?void 0:snapshot.rows.find(row=>row.index===snapshot.activeCell.rowIndex);const activeRects=activeMerges.length>0?activeMerges:activeColumn!==void 0&&activeRow!==void 0?[{x:activeColumn.x,y:activeRow.y,width:activeColumn.width,height:activeRow.height}]:[];for(const activeRect of activeRects){const activeX=Math.max(snapshot.rowGutterWidth,activeRect.x);const activeY=Math.max(snapshot.headerHeight,activeRect.y);const activeRight=Math.min(snapshot.width,activeRect.x+activeRect.width);const activeBottom=Math.min(snapshot.height,activeRect.y+activeRect.height);context.strokeStyle=theme.activeCellBorder;context.lineWidth=this.#forcedColors?3:2;context.setLineDash([]);context.strokeRect(activeX+context.lineWidth/2,activeY+context.lineWidth/2,Math.max(0,activeRight-activeX-context.lineWidth),Math.max(0,activeBottom-activeY-context.lineWidth))}}};function cssColor(color){const value=color?.css?.trim();if(!value)return void 0;const supports=globalThis.CSS?.supports;return typeof supports!=="function"||supports("color",value)?value:void 0}function finitePositive(value){return value!==void 0&&Number.isFinite(value)&&value>0?value:void 0}function horizontalAlignment(style){switch(style?.horizontalAlignment){case"center":return"center";case"right":return"right";case"left":case"justify":case"general":default:return"left"}}function verticalBaseline(style){switch(style?.verticalAlignment){case"top":return"top";case"bottom":return"bottom";case"center":case"justify":default:return"middle"}}function horizontalTextX(alignment,x,width,padding){if(alignment==="center")return x+width/2;if(alignment==="right"||alignment==="end")return x+width-padding;return x+padding}function verticalTextY(baseline,y,height,padding){if(baseline==="top"||baseline==="hanging")return y+padding;if(baseline==="bottom"||baseline==="ideographic")return y+height-padding;return y+height/2}function underlineStart(alignment,textX,width){if(alignment==="center")return textX-width/2;if(alignment==="right"||alignment==="end")return textX-width;return textX}function borderWidth(style){switch(style){case"medium":return 2;case"thick":return 3;case"double":return 1;default:return 1}}function borderDash(style){if(style==="dashed")return[4,2];if(style==="dotted")return[1,2];return[]}function positive(value,fallback){return value!==void 0&&Number.isFinite(value)&&value>0?value:fallback}var TabularkError=class _TabularkError extends Error{code;retryable;details;constructor(code,message,options={}){super(message,{cause:options.cause});this.name="TabularkError";this.code=code;this.retryable=options.retryable??false;this.details=code==="RESOURCE_LIMIT"?normalizeResourceLimitDetails(options.details):options.details}static fromSerialized(error){return new _TabularkError(error.code,error.message,{retryable:error.retryable,details:error.details})}};function normalizeResourceLimitDetails(details){const raw=isRecord(details)?details:{};const resource=typeof raw.resource==="string"&&raw.resource.length>0?raw.resource:typeof raw.resourceCategory==="string"&&raw.resourceCategory.length>0?raw.resourceCategory:inferResource(raw);if(isQuantity(raw.requiredBytes)&&isQuantity(raw.availableBytes)){return Object.freeze({...raw,resource})}if(isQuantity(raw.required)&&isQuantity(raw.available)){return Object.freeze({...raw,resource})}const byteLimit=firstQuantity(raw,["maxDecodedBytes","maxOutputBytes","maxMetadataBytes","maxBlockBytes","maxDisplayCellBytes","maxDisplayBytes","maxSourceBytes","maxFieldBytes","maxBatchBytes","maxOperationBytes","maxZipEntryBytes","maxZipUncompressedBytes","maxCfbStreamBytes"]);if(byteLimit!==void 0){const requiredBytes=firstQuantity(raw,["decodedBytes","outputBytes","metadataBytes","blockBytes","displayCellBytes","displayBytes","sourceBytes","fieldBytes","batchBytes","operationBytes"])??byteLimit+1;return Object.freeze({...raw,resource,requiredBytes,availableBytes:byteLimit})}const available=firstQuantity(raw,["limit","maxCells","maxRangeCells","maxSources","maxActiveRanges","maxRangeWaiters","maxColumns","maxFields","maxNestingDepth","maxWorksheetRows","maxWorksheetColumns","maxStyles","maxMergedRegions"])??0;const required=firstQuantity(raw,["cells","cellCount","visibleCellCount","sources","activeRanges","rangeWaiters","columns","fields","nestingDepth","worksheetRows","worksheetColumns","styles","mergedRegions"])??available+1;return Object.freeze({...raw,resource,required,available})}function inferResource(details){const keys=Object.keys(details).join(" ").toLowerCase();if(keys.includes("source"))return keys.includes("byte")?"source-staging":"source-slots";if(keys.includes("metadata"))return"metadata";if(keys.includes("decoded")||keys.includes("decompress"))return"decompression";if(keys.includes("display"))return"display-values";if(keys.includes("field"))return"field";if(keys.includes("batch"))return"batch";if(keys.includes("range")||keys.includes("cell"))return"range-cells";if(keys.includes("column"))return"columns";if(keys.includes("nest"))return"descriptor-nesting";if(keys.includes("worksheet"))return"worksheet-dimensions";return"adapter-resource"}function firstQuantity(details,names){for(const name of names){if(isQuantity(details[name]))return details[name]}return void 0}function isQuantity(value){return typeof value==="number"&&Number.isFinite(value)&&value>=0}function isRecord(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}function closedError(resource){return new TabularkError("HANDLE_CLOSED",`${resource} is closed`)}function invalidArgument(message,details){return new TabularkError("INVALID_ARGUMENT",message,{details})}var DEFAULT_MEMORY_BUDGET_BYTES=256*1024*1024;var MAX_ARRAY_BUFFER_BYTES=128*1024*1024;var MAX_RANGE_CELLS=25e4;var INDEX_BUDGET_MAX_BYTES=64*1024*1024;var ADAPTER_TILE_CACHE_MAX_BYTES=96*1024*1024;var MAIN_THREAD_RANGE_CACHE_MAX_BYTES=32*1024*1024;var FIELD_AND_BATCH_MAX_BYTES=8*1024*1024;var UTF8_DECODER=new TextDecoder;var DEFAULT_ROW_HEIGHT=28;var DEFAULT_HEADER_HEIGHT=36;var DEFAULT_ROW_HEADER_WIDTH=64;var DEFAULT_COLUMN_WIDTH=160;var DEFAULT_MIN_COLUMN_WIDTH=64;var DEFAULT_MAX_COLUMN_WIDTH=640;var DEFAULT_SCROLL_PIXEL_LIMIT=16e6;var DEFAULT_OVERSCAN_ROWS=8;var DEFAULT_OVERSCAN_COLUMNS=1;function createTableLayout(metadata,columnWidths,viewport,options={}){const rowHeight=positiveFinite(options.rowHeight??DEFAULT_ROW_HEIGHT,"rowHeight");const headerHeight=nonNegativeFinite(options.headerHeight??DEFAULT_HEADER_HEIGHT,"headerHeight");const rowHeaderWidth=nonNegativeFinite(options.rowHeaderWidth??DEFAULT_ROW_HEADER_WIDTH,"rowHeaderWidth");const scrollPixelLimit=positiveFinite(options.scrollPixelLimit??DEFAULT_SCROLL_PIXEL_LIMIT,"scrollPixelLimit");const overscanRows=nonNegativeInteger(options.overscanRows??DEFAULT_OVERSCAN_ROWS,"overscanRows");const overscanColumns=nonNegativeInteger(options.overscanColumns??DEFAULT_OVERSCAN_COLUMNS,"overscanColumns");const width=nonNegativeFinite(viewport.width,"viewport.width");const height=nonNegativeFinite(viewport.height,"viewport.height");const devicePixelRatio=positiveFinite(viewport.devicePixelRatio??1,"viewport.devicePixelRatio");const columnCount=metadata.schema.columns.length;if(columnWidths.length!==columnCount){throw invalidArgument(`columnWidths has ${columnWidths.length} entries; expected ${columnCount}`)}for(let index=0;index<columnWidths.length;index+=1){positiveFinite(columnWidths[index],`columnWidths[${index}]`)}const hiddenColumns=options.hiddenColumns??[];if(hiddenColumns.length!==0&&hiddenColumns.length!==columnCount){throw invalidArgument(`hiddenColumns has ${hiddenColumns.length} entries; expected ${columnCount}`)}const effectiveColumnWidths=Object.freeze(columnWidths.map((width2,index)=>hiddenColumns[index]===true?0:width2));const rowCount=extentValue(metadata.extent.rows);const frozenRowCount=Math.min(rowCount,nonNegativeInteger(options.frozenRows??0,"frozenRows"));const frozenColumnCount=Math.min(columnCount,nonNegativeInteger(options.frozenColumns??0,"frozenColumns"));const bodyWidth=Math.max(0,width-rowHeaderWidth);const bodyHeight=Math.max(0,height-headerHeight);const prefixes=columnOffsets(effectiveColumnWidths);const rowGeometry=createSparseAxisGeometry(rowCount,rowHeight,options.rowEntries??[]);const logicalWidth=prefixes[prefixes.length-1]??0;const logicalHeight=rowGeometry.contentSize;const horizontal=createScrollAxis(logicalWidth,bodyWidth,viewport.scrollLeft,scrollPixelLimit);const vertical=createScrollAxis(logicalHeight,bodyHeight,viewport.scrollTop,scrollPixelLimit);const visibleRows=visibleRowRange(rowGeometry,vertical.logicalOffset,bodyHeight);const visibleColumns=visibleColumnRange(prefixes,horizontal.logicalOffset,bodyWidth);const overscanRowRange=expandRange(visibleRows,overscanRows,rowCount);const overscanColumnRange=expandRange(visibleColumns,overscanColumns,columnCount);const frozenRowExtent=axisPosition(rowGeometry,frozenRowCount);const frozenColumnExtent=prefixes[frozenColumnCount]??0;const frozenRowRange=frozenRowCount===0?Object.freeze({start:0,end:0}):clampRangeEnd(visibleRowRange(rowGeometry,0,Math.min(bodyHeight,frozenRowExtent)),frozenRowCount);const frozenColumnRange=frozenColumnCount===0?Object.freeze({start:0,end:0}):clampRangeEnd(visibleColumnRange(prefixes,0,Math.min(bodyWidth,frozenColumnExtent)),frozenColumnCount);return Object.freeze({width,height,devicePixelRatio,rowHeight,headerHeight,rowHeaderWidth,bodyWidth,bodyHeight,rowCount,columnCount,rows:Object.freeze({visible:visibleRows,overscan:overscanRowRange}),columns:Object.freeze({visible:visibleColumns,overscan:overscanColumnRange}),visibleColumns:materializeColumns(metadata,effectiveColumnWidths,prefixes,visibleColumns,frozenColumnRange,rowHeaderWidth,horizontal.logicalOffset,frozenColumnCount),overscanColumns:materializeColumns(metadata,effectiveColumnWidths,prefixes,overscanColumnRange,frozenColumnRange,rowHeaderWidth,horizontal.logicalOffset,frozenColumnCount),visibleRows:materializeRows(rowGeometry,visibleRows,frozenRowRange,headerHeight,vertical.logicalOffset,frozenRowCount),overscanRows:materializeRows(rowGeometry,overscanRowRange,frozenRowRange,headerHeight,vertical.logicalOffset,frozenRowCount),effectiveColumnWidths,rowGeometry,frozenRowCount,frozenColumnCount,frozenRowExtent,frozenColumnExtent,frozenRowRange,frozenColumnRange,horizontal,vertical,spacerWidth:rowHeaderWidth+horizontal.physicalContentSize,spacerHeight:headerHeight+vertical.physicalContentSize})}function createScrollAxis(logicalContentSize,logicalViewportSize,physicalOffset,pixelLimit=DEFAULT_SCROLL_PIXEL_LIMIT){nonNegativeFinite(logicalContentSize,"logicalContentSize");nonNegativeFinite(logicalViewportSize,"logicalViewportSize");positiveFinite(pixelLimit,"pixelLimit");const logicalMaxOffset=Math.max(0,logicalContentSize-logicalViewportSize);const largestUsableSize=Math.max(pixelLimit,logicalViewportSize+1);const physicalContentSize=Math.min(logicalContentSize,largestUsableSize);const physicalMaxOffset=Math.max(0,physicalContentSize-logicalViewportSize);const normalizedPhysicalOffset=clampFinite(physicalOffset,0,physicalMaxOffset);const compressed=logicalMaxOffset>physicalMaxOffset;const logicalOffset=physicalMaxOffset===0?0:compressed?normalizedPhysicalOffset/physicalMaxOffset*logicalMaxOffset:normalizedPhysicalOffset;return Object.freeze({logicalContentSize,logicalViewportSize,logicalOffset,logicalMaxOffset,physicalContentSize,physicalOffset:normalizedPhysicalOffset,physicalMaxOffset,compressed})}function logicalToPhysicalOffset(axis,logicalOffset){const normalized=clampFinite(logicalOffset,0,axis.logicalMaxOffset);if(!axis.compressed){return Math.min(normalized,axis.physicalMaxOffset)}return axis.logicalMaxOffset===0?0:normalized/axis.logicalMaxOffset*axis.physicalMaxOffset}function physicalToLogicalOffset(axis,physicalOffset){const normalized=clampFinite(physicalOffset,0,axis.physicalMaxOffset);if(!axis.compressed){return Math.min(normalized,axis.logicalMaxOffset)}return axis.physicalMaxOffset===0?0:normalized/axis.physicalMaxOffset*axis.logicalMaxOffset}function hitTest(layout,x,y,_columnWidths,resizeHandleWidth=6){if(!Number.isFinite(x)||!Number.isFinite(y)||x<0||y<0||x>=layout.width||y>=layout.height){return Object.freeze({kind:"outside"})}const columnWidths=layout.effectiveColumnWidths;if(x<layout.rowHeaderWidth&&y<layout.headerHeight){return Object.freeze({kind:"corner"})}if(y<layout.headerHeight){const offsets=columnOffsets(columnWidths);const logicalX=x-layout.rowHeaderWidth+layout.horizontal.logicalOffset;const nextBoundary=lowerBound(offsets,logicalX);const handleWidth=Math.max(0,resizeHandleWidth);for(const boundaryIndex of[nextBoundary,nextBoundary-1]){if(boundaryIndex<=0||boundaryIndex>=offsets.length){continue}const boundaryX=layout.rowHeaderWidth+offsets[boundaryIndex]-layout.horizontal.logicalOffset;if(Math.abs(x-boundaryX)<=handleWidth){return Object.freeze({kind:"column-resize",columnIndex:boundaryIndex-1,boundaryX})}}const columnIndex2=columnAtViewportX(layout,x,columnWidths);if(columnIndex2===null){return Object.freeze({kind:"outside"})}return Object.freeze({kind:"column-header",columnIndex:columnIndex2})}const rowIndex=rowAtViewportY(layout,y);if(rowIndex===null){return Object.freeze({kind:"outside"})}if(x<layout.rowHeaderWidth){return Object.freeze({kind:"row-header",rowIndex})}const columnIndex=columnAtViewportX(layout,x,columnWidths);return columnIndex===null?Object.freeze({kind:"outside"}):Object.freeze({kind:"cell",rowIndex,columnIndex})}function cellRect(layout,_columnWidths,rowIndex,columnIndex){const columnWidths=layout.effectiveColumnWidths;const offsets=columnOffsets(columnWidths);const rowOffset=axisPosition(layout.rowGeometry,rowIndex);const columnOffset=offsets[columnIndex]??0;return Object.freeze({x:layout.rowHeaderWidth+columnOffset-(columnIndex<layout.frozenColumnCount?0:layout.horizontal.logicalOffset),y:layout.headerHeight+rowOffset-(rowIndex<layout.frozenRowCount?0:layout.vertical.logicalOffset),width:columnWidths[columnIndex]??0,height:axisSize(layout.rowGeometry,rowIndex)})}function columnHeaderRect(layout,_columnWidths,columnIndex){const columnWidths=layout.effectiveColumnWidths;const offsets=columnOffsets(columnWidths);return Object.freeze({x:layout.rowHeaderWidth+offsets[columnIndex]-(columnIndex<layout.frozenColumnCount?0:layout.horizontal.logicalOffset),y:0,width:columnWidths[columnIndex]??0,height:layout.headerHeight})}function rowHeaderRect(layout,rowIndex){const rowOffset=axisPosition(layout.rowGeometry,rowIndex);return Object.freeze({x:0,y:layout.headerHeight+rowOffset-(rowIndex<layout.frozenRowCount?0:layout.vertical.logicalOffset),width:layout.rowHeaderWidth,height:axisSize(layout.rowGeometry,rowIndex)})}function selectionRect(layout,_columnWidths,range){const columnWidths=layout.effectiveColumnWidths;if(range.rowStart>=range.rowEnd||range.columnStart>=range.columnEnd){return null}const offsets=columnOffsets(columnWidths);const columnStart=clampInteger(range.columnStart,0,columnWidths.length);const columnEnd=clampInteger(range.columnEnd,columnStart,columnWidths.length);const rowStart=clampInteger(range.rowStart,0,layout.rowCount);const rowEnd=clampInteger(range.rowEnd,rowStart,layout.rowCount);if(columnStart===columnEnd||rowStart===rowEnd){return null}const frozenColumns=columnEnd<=layout.frozenColumnCount;const frozenRows=rowEnd<=layout.frozenRowCount;const rowStartOffset=axisPosition(layout.rowGeometry,rowStart);const rowEndOffset=axisPosition(layout.rowGeometry,rowEnd);return Object.freeze({x:layout.rowHeaderWidth+offsets[columnStart]-(frozenColumns?0:layout.horizontal.logicalOffset),y:layout.headerHeight+rowStartOffset-(frozenRows?0:layout.vertical.logicalOffset),width:offsets[columnEnd]-offsets[columnStart],height:rowEndOffset-rowStartOffset})}function columnOffsets(columnWidths){const offsets=new Array(columnWidths.length+1);offsets[0]=0;for(let index=0;index<columnWidths.length;index+=1){offsets[index+1]=offsets[index]+(columnWidths[index]??DEFAULT_COLUMN_WIDTH)}return offsets}function extentValue(extent){return extent.kind==="unknown"?0:extent.value}function createSparseAxisGeometry(count,defaultSize,entries=[]){nonNegativeInteger(count,"axis count");positiveFinite(defaultSize,"axis defaultSize");const byIndex=new Map;for(const entry of entries){if(!entry||!Number.isSafeInteger(entry.index)||entry.index<0||entry.index>=count){continue}if(entry.hidden===true){byIndex.set(entry.index,0);continue}if(entry.size!==void 0){if(!Number.isFinite(entry.size)||entry.size<0){continue}byIndex.set(entry.index,entry.size)}}let cumulativeDelta=0;const overrides=[...byIndex.entries()].sort(([left],[right])=>left-right).map(([index,size])=>{cumulativeDelta+=size-defaultSize;return Object.freeze({index,size,cumulativeDelta})});const contentSize=count*defaultSize+cumulativeDelta;return Object.freeze({count,defaultSize,contentSize:Math.max(0,contentSize),overrides:Object.freeze(overrides)})}function axisPosition(geometry,index){const normalized=clampInteger(index,0,geometry.count);const overrideIndex=lowerBoundOverride(geometry.overrides,normalized);const delta=overrideIndex===0?0:geometry.overrides[overrideIndex-1].cumulativeDelta;return normalized*geometry.defaultSize+delta}function axisSize(geometry,index){if(!Number.isSafeInteger(index)||index<0||index>=geometry.count){return 0}const match=sparseOverrideAt(geometry,index);return match?.size??geometry.defaultSize}function axisIndexAtOffset(geometry,offset){if(!Number.isFinite(offset)||offset<0||offset>=geometry.contentSize||geometry.count===0){return null}let low=0;let high=geometry.count;while(low<high){const middle=Math.floor((low+high)/2);if(axisPosition(geometry,middle+1)<=offset){low=middle+1}else{high=middle}}for(let index=low;index<geometry.count;index+=1){const size=axisSize(geometry,index);const start=axisPosition(geometry,index);if(size>0&&offset>=start&&offset<start+size){return index}if(start>offset){return null}}return null}function nextVisibleAxisIndex(geometry,from,direction=1){for(let index=clampInteger(from,0,Math.max(0,geometry.count-1));index>=0&&index<geometry.count;index+=direction){if(axisSize(geometry,index)>0){return index}}return null}function sparseOverrideAt(geometry,index){const low=lowerBoundOverride(geometry.overrides,index);const candidate=geometry.overrides[low];return candidate?.index===index?candidate:void 0}function lowerBoundOverride(entries,target){let low=0;let high=entries.length;while(low<high){const middle=Math.floor((low+high)/2);if(entries[middle].index<target)low=middle+1;else high=middle}return low}function clampRangeEnd(range,end){return Object.freeze({start:Math.min(range.start,end),end:Math.min(range.end,end)})}function visibleRowRange(geometry,scrollTop,viewportHeight){if(geometry.count===0||viewportHeight<=0||geometry.contentSize<=0){return Object.freeze({start:0,end:0})}const start=axisIndexAtOffset(geometry,scrollTop);if(start===null){return Object.freeze({start:geometry.count,end:geometry.count})}const last=axisIndexAtOffset(geometry,Math.max(scrollTop,Math.min(geometry.contentSize,scrollTop+viewportHeight)-Math.max(1e-7,Math.abs(scrollTop+viewportHeight)*Number.EPSILON*4)));return Object.freeze({start,end:last===null?start+1:Math.min(geometry.count,last+1)})}function visibleColumnRange(offsets,scrollLeft,viewportWidth){const count=Math.max(0,offsets.length-1);if(count===0||viewportWidth<=0){return Object.freeze({start:0,end:0})}const start=columnAtLogicalX(offsets,scrollLeft)??count;const end=Math.min(count,lowerBound(offsets,scrollLeft+viewportWidth));return Object.freeze({start,end:Math.max(start,end)})}function expandRange(range,amount,limit){return Object.freeze({start:Math.max(0,range.start-amount),end:Math.min(limit,range.end+amount)})}function materializeColumns(metadata,widths,offsets,range,frozenRange,rowHeaderWidth,scrollLeft,frozenColumnCount){const indexes=rangeIndexes(range,frozenRange);const columns=[];for(const index of indexes){const column=metadata.schema.columns[index];const width=widths[index]??0;if(width<=0){continue}const frozen=index<frozenColumnCount;columns.push(Object.freeze({index,id:column.id,name:column.name,x:rowHeaderWidth+offsets[index]-(frozen?0:scrollLeft),width,frozen}))}return Object.freeze(columns)}function materializeRows(geometry,range,frozenRange,headerHeight,scrollTop,frozenRowCount){const rows=[];for(const index of rangeIndexes(range,frozenRange)){const height=axisSize(geometry,index);if(height<=0){continue}const frozen=index<frozenRowCount;rows.push(Object.freeze({index,y:headerHeight+axisPosition(geometry,index)-(frozen?0:scrollTop),height,frozen}))}return Object.freeze(rows)}function rangeIndexes(primary,frozen){const indexes=new Set;for(let index=primary.start;index<primary.end;index+=1){indexes.add(index)}for(let index=frozen.start;index<frozen.end;index+=1){indexes.add(index)}return Object.freeze([...indexes].sort((left,right)=>left-right))}function columnAtViewportX(layout,x,columnWidths){if(x<layout.rowHeaderWidth){return null}const bodyX=x-layout.rowHeaderWidth;if(bodyX<Math.min(layout.bodyWidth,layout.frozenColumnExtent)){const frozen=columnAtLogicalX(columnOffsets(columnWidths),bodyX);if(frozen!==null&&frozen<layout.frozenColumnCount){return frozen}}return columnAtLogicalX(columnOffsets(columnWidths),bodyX+layout.horizontal.logicalOffset)}function columnAtLogicalX(offsets,x){const count=Math.max(0,offsets.length-1);if(x<0||count===0||x>=offsets[count]){return null}let low=0;let high=count;while(low<high){const middle=Math.floor((low+high)/2);if(offsets[middle+1]<=x){low=middle+1}else{high=middle}}return low}function rowAtViewportY(layout,y){const bodyY=y-layout.headerHeight;if(bodyY<Math.min(layout.bodyHeight,layout.frozenRowExtent)){const frozen=axisIndexAtOffset(layout.rowGeometry,bodyY);if(frozen!==null&&frozen<layout.frozenRowCount){return frozen}}return axisIndexAtOffset(layout.rowGeometry,bodyY+layout.vertical.logicalOffset)}function lowerBound(values,target){let low=0;let high=values.length;while(low<high){const middle=Math.floor((low+high)/2);if(values[middle]<target){low=middle+1}else{high=middle}}return low}function positiveFinite(value,name){if(!Number.isFinite(value)||value<=0){throw invalidArgument(`${name} must be a positive finite number`)}return value}function nonNegativeFinite(value,name){if(!Number.isFinite(value)||value<0){throw invalidArgument(`${name} must be a non-negative finite number`)}return value}function nonNegativeInteger(value,name){if(!Number.isSafeInteger(value)||value<0){throw invalidArgument(`${name} must be a non-negative safe integer`)}return value}function clampFinite(value,minimum,maximum){if(!Number.isFinite(value)){return minimum}return Math.min(maximum,Math.max(minimum,value))}function clampInteger(value,minimum,maximum){if(!Number.isFinite(value)){return minimum}return Math.min(maximum,Math.max(minimum,Math.trunc(value)))}function createSelection(anchor,focus=anchor){assertCellPosition(anchor,"anchor");assertCellPosition(focus,"focus");const normalizedAnchor=Object.freeze({...anchor});const normalizedFocus=Object.freeze({...focus});return Object.freeze({anchor:normalizedAnchor,focus:normalizedFocus,range:selectionRange(normalizedAnchor,normalizedFocus)})}function selectionRange(anchor,focus){return Object.freeze({rowStart:Math.min(anchor.rowIndex,focus.rowIndex),rowEnd:Math.max(anchor.rowIndex,focus.rowIndex)+1,columnStart:Math.min(anchor.columnIndex,focus.columnIndex),columnEnd:Math.max(anchor.columnIndex,focus.columnIndex)+1})}function containsCell(range,cell){return cell.rowIndex>=range.rowStart&&cell.rowIndex<range.rowEnd&&cell.columnIndex>=range.columnStart&&cell.columnIndex<range.columnEnd}function clampCell(cell,rowCount,columnCount){if(rowCount<=0||columnCount<=0){return null}return Object.freeze({rowIndex:clampInteger2(cell.rowIndex,0,rowCount-1),columnIndex:clampInteger2(cell.columnIndex,0,columnCount-1)})}function moveCell(cell,command,rowCount,columnCount,pageRowCount){const current=clampCell(cell,rowCount,columnCount);if(current===null){return null}const page=Math.max(1,Math.floor(pageRowCount));switch(command){case"left":return clampCell({rowIndex:current.rowIndex,columnIndex:current.columnIndex-1},rowCount,columnCount);case"right":return clampCell({rowIndex:current.rowIndex,columnIndex:current.columnIndex+1},rowCount,columnCount);case"up":return clampCell({rowIndex:current.rowIndex-1,columnIndex:current.columnIndex},rowCount,columnCount);case"down":return clampCell({rowIndex:current.rowIndex+1,columnIndex:current.columnIndex},rowCount,columnCount);case"page-up":return clampCell({rowIndex:current.rowIndex-page,columnIndex:current.columnIndex},rowCount,columnCount);case"page-down":return clampCell({rowIndex:current.rowIndex+page,columnIndex:current.columnIndex},rowCount,columnCount);case"row-start":return Object.freeze({rowIndex:current.rowIndex,columnIndex:0});case"row-end":return Object.freeze({rowIndex:current.rowIndex,columnIndex:columnCount-1});case"table-start":return Object.freeze({rowIndex:0,columnIndex:0});case"table-end":return Object.freeze({rowIndex:rowCount-1,columnIndex:columnCount-1})}}function assertCellPosition(value,name){if(!Number.isSafeInteger(value.rowIndex)||value.rowIndex<0){throw invalidArgument(`${name}.rowIndex must be a non-negative safe integer`)}if(!Number.isSafeInteger(value.columnIndex)||value.columnIndex<0){throw invalidArgument(`${name}.columnIndex must be a non-negative safe integer`)}}function clampInteger2(value,minimum,maximum){if(!Number.isFinite(value)){return minimum}return Math.min(maximum,Math.max(minimum,Math.trunc(value)))}var DEFAULT_MAX_WINDOW_CELLS=1e5;var Controller=class{#table;#options;#listeners=new Set;#unsubscribe;#viewport={width:0,height:0,scrollLeft:0,scrollTop:0,devicePixelRatio:1};#columnWidths;#hiddenColumns;#presentationRows=[];#frozenRows=0;#frozenColumns=0;#mergedCells=[];#snapshot;#activeCell=null;#selection=null;#windows=[];#loadingRanges=[];#requestAbort=null;#generation=0;#loadQueued=false;#disposed=false;#tableClosed=false;constructor(table,options){this.#table=table;this.#options=normalizeOptions(options);this.#columnWidths=initialColumnWidths(table.metadata,this.#options);this.#hiddenColumns=Array.from({length:this.#columnWidths.length},()=>false);const layout=createTableLayout(table.metadata,this.#columnWidths,this.#viewport,this.#layoutOptions());this.#snapshot=Object.freeze({generation:this.#generation,status:"loading",metadata:table.metadata,layout,activeCell:null,selection:null});this.#unsubscribe=table.subscribe(event=>this.#handleTableEvent(event))}get metadata(){return this.#table.metadata}get columnWidths(){return Object.freeze([...this.#columnWidths])}get minColumnWidth(){return this.#options.minColumnWidth}get maxColumnWidth(){return this.#options.maxColumnWidth}getSnapshot(){return this.#snapshot}subscribe(listener){this.#assertOpen();this.#listeners.add(listener);return()=>this.#listeners.delete(listener)}updateViewport(viewport){this.#assertOpen();const normalized=normalizeViewport(viewport);if(sameViewport(normalized,this.#viewport)){return}this.#viewport=normalized;this.#advanceGeneration()}setActiveCell(cell,options={}){this.#assertOpen();assertCellPosition(cell,"cell");const clamped=clampCell(cell,this.#snapshot.layout.rowCount,this.#snapshot.layout.columnCount);if(clamped===null){return}const next=this.#visibleCell(this.#mergeAnchor(clamped),1,1);if(next===null)return;const anchor=options.extendSelection&&this.#selection!==null?this.#selection.anchor:next;this.#activeCell=next;this.#selection=createMergedSelection(anchor,next,this.#mergedCells);if(options.scrollIntoView!==false&&this.#scrollCellIntoView(next)){this.#advanceGeneration();return}this.#publish()}extendSelection(cell,options={}){this.setActiveCell(cell,{...options,extendSelection:true})}setSelection(anchor,focus=anchor){this.#assertOpen();assertCellPosition(anchor,"anchor");assertCellPosition(focus,"focus");const rawAnchor=clampCell(anchor,this.#snapshot.layout.rowCount,this.#snapshot.layout.columnCount);const rawFocus=clampCell(focus,this.#snapshot.layout.rowCount,this.#snapshot.layout.columnCount);if(rawAnchor===null||rawFocus===null){this.clearSelection();return}const clampedAnchor=this.#visibleCell(this.#mergeAnchor(rawAnchor),1,1);const clampedFocus=this.#visibleCell(this.#mergeAnchor(rawFocus),1,1);if(clampedAnchor===null||clampedFocus===null){this.clearSelection();return}this.#activeCell=clampedFocus;this.#selection=createMergedSelection(clampedAnchor,clampedFocus,this.#mergedCells);this.#publish()}clearSelection(){this.#assertOpen();if(this.#selection===null){return}this.#selection=null;this.#publish()}moveActive(command,options={}){this.#assertOpen();const current=this.#activeCell??{rowIndex:0,columnIndex:0};const origin=navigationOrigin(current,command,this.#mergedCells);const moved=moveCell(origin,command,this.#snapshot.layout.rowCount,this.#snapshot.layout.columnCount,Math.max(1,this.#snapshot.layout.visibleRows.filter(row=>!row.frozen).length));const[rowDirection,columnDirection]=navigationDirections(command);const next=moved===null?null:this.#visibleCell(this.#mergeAnchor(moved),rowDirection,columnDirection);if(next!==null){this.setActiveCell(next,options)}}ensureCellVisible(cell){this.#assertOpen();assertCellPosition(cell,"cell");const next=clampCell(cell,this.#snapshot.layout.rowCount,this.#snapshot.layout.columnCount);if(next!==null&&this.#scrollCellIntoView(next)){this.#advanceGeneration()}}ensureColumnVisible(columnIndex){this.#assertOpen();assertColumnIndex(columnIndex,this.#columnWidths.length);if(this.#scrollColumnIntoView(columnIndex)){this.#advanceGeneration()}}resizeColumn(columnIndex,width){this.#assertOpen();assertColumnIndex(columnIndex,this.#columnWidths.length);const normalized=clampWidth(width,this.#options);if(this.#columnWidths[columnIndex]===normalized){return}this.#columnWidths[columnIndex]=normalized;this.#advanceGeneration()}autosizeColumn(columnIndex,measuredWidth){this.resizeColumn(columnIndex,measuredWidth)}applySpreadsheetPresentation(presentation){this.#assertOpen();if(presentation.kind!=="spreadsheet-v1"||presentation.tableId!==this.metadata.tableId){throw invalidArgument("presentation must belong to the controller table")}const nextWidths=[...this.#columnWidths];const hiddenColumns=Array.from({length:nextWidths.length},()=>false);for(const entry of presentation.columns){if(!Number.isSafeInteger(entry.index)||entry.index<0||entry.index>=nextWidths.length){continue}hiddenColumns[entry.index]=entry.hidden===true;if(entry.size!==void 0&&Number.isFinite(entry.size)&&entry.size>0){nextWidths[entry.index]=clampWidth(entry.size,this.#options)}}this.#columnWidths=nextWidths;this.#hiddenColumns=hiddenColumns;this.#presentationRows=Object.freeze(presentation.rows.map(entry=>Object.freeze({...entry})));this.#frozenRows=presentation.frozenRows;this.#frozenColumns=presentation.frozenColumns;this.#mergedCells=[];this.#advanceGeneration();this.#normalizeInteractionForPresentation();this.#publish()}setMergedCells(regions){this.#assertOpen();const normalized=normalizeMergedCells(regions,this.#snapshot.layout.rowCount,this.#snapshot.layout.columnCount);if(sameMergedCells(normalized,this.#mergedCells)){return}this.#mergedCells=normalized;this.#normalizeInteractionForPresentation();this.#advanceGeneration()}hitTest(x,y,resizeHandleWidth){const result=hitTest(this.#snapshot.layout,x,y,this.#columnWidths,resizeHandleWidth);if(result.kind!=="cell"){return result}const anchor=this.#mergeAnchor(result);return Object.freeze({kind:"cell",...anchor})}cellRect(cell){assertCellPosition(cell,"cell");if(cell.rowIndex>=this.#snapshot.layout.rowCount||cell.columnIndex>=this.#snapshot.layout.columnCount){throw invalidArgument("cell must be inside the current table extent")}return cellRect(this.#snapshot.layout,this.#columnWidths,cell.rowIndex,cell.columnIndex)}selectionRect(selection=this.#selection){return selection===null?null:selectionRect(this.#snapshot.layout,this.#columnWidths,selection.range)}getCell(rowIndex,columnIndex){if(!isValidCell(rowIndex,columnIndex,this.#snapshot.layout.rowCount,this.#columnWidths.length)){return Object.freeze({status:"unavailable"})}for(const window of this.#windows){if(rangeContains(window.range,rowIndex,columnIndex)){return Object.freeze({status:"loaded",value:window.rows[rowIndex-window.range.rowStart][columnIndex-window.range.columnStart]})}}for(const range of this.#loadingRanges){if(rangeContains(range,rowIndex,columnIndex)){return Object.freeze({status:"loading"})}}return Object.freeze({status:"unavailable"})}async copySelection(options={}){this.#assertOpen();const selection=this.#selection;if(selection===null){return""}const request=gridRangeToRequest(selection.range);assertWindowCellLimit(request,MAX_RANGE_CELLS,"selection");const batch=await this.#table.readRange(request,options.signal?{signal:options.signal}:{});return rowsToTsv(batchRows(batch))}dispose(){if(this.#disposed){return}this.#disposed=true;this.#requestAbort?.abort();this.#requestAbort=null;this.#unsubscribe();this.#listeners.clear();this.#windows=[];this.#loadingRanges=[];this.#snapshot=Object.freeze({...this.#snapshot,status:"closed",activeCell:null,selection:null})}#advanceGeneration(){this.#generation+=1;this.#requestAbort?.abort();this.#requestAbort=null;this.#loadingRanges=[];this.#rebuildSnapshot("loading");this.#queueLoad()}#queueLoad(){if(this.#loadQueued||this.#disposed){return}this.#loadQueued=true;queueMicrotask(()=>{this.#loadQueued=false;if(!this.#disposed){void this.#loadGeneration(this.#generation)}})}async#loadGeneration(generation){let visible;let overscan;try{({visible,overscan}=layoutRanges(this.#snapshot.layout,this.#options.maxWindowCells,this.#mergedCells))}catch(error){if(!this.#disposed&&generation===this.#generation){this.#loadingRanges=[];this.#rebuildSnapshot("error",error)}return}if(visible.length===0){this.#windows=[];this.#loadingRanges=[];this.#rebuildSnapshot("ready");return}const abort=new AbortController;this.#requestAbort=abort;const ranges=Object.freeze([...visible,...overscan.filter(candidate=>!visible.some(required=>rangeContainsRange(candidate,required)&&sameGridRange(candidate,required)))]);this.#loadingRanges=Object.freeze(ranges);this.#publish();let loaded=[];try{for(let index=0;index<ranges.length;index+=1){const range=ranges[index];const batch=await this.#table.readRange(gridRangeToRequest(range),{signal:abort.signal});if(this.#disposed||generation!==this.#generation||abort.signal.aborted){return}const returnedRange=batchGridRange(batch);if(returnedRange.rowStart<returnedRange.rowEnd&&returnedRange.columnStart<returnedRange.columnEnd){const window=Object.freeze({range:returnedRange,rows:batchRows(batch)});loaded=[window,...loaded.filter(candidate=>!rangeContainsRange(returnedRange,candidate.range))]}this.#windows=Object.freeze([...loaded]);this.#loadingRanges=Object.freeze(ranges.slice(index+1));this.#rebuildSnapshot("ready")}}catch(error){if(this.#disposed||generation!==this.#generation||abort.signal.aborted||error instanceof TabularkError&&error.code==="CANCELLED"){return}this.#loadingRanges=[];this.#rebuildSnapshot("error",error)}finally{if(this.#requestAbort===abort){this.#requestAbort=null}}}#scrollCellIntoView(cell){const layout=this.#snapshot.layout;const offsets=columnOffsets(layout.effectiveColumnWidths);const cellLeft=offsets[cell.columnIndex];const cellRight=offsets[cell.columnIndex+1];const cellTop=axisPosition(layout.rowGeometry,cell.rowIndex);const cellBottom=cellTop+axisSize(layout.rowGeometry,cell.rowIndex);const logicalLeft=cell.columnIndex<layout.frozenColumnCount?layout.horizontal.logicalOffset:revealOffset(layout.horizontal.logicalOffset,layout.bodyWidth,cellLeft,cellRight,layout.horizontal.logicalMaxOffset);const logicalTop=cell.rowIndex<layout.frozenRowCount?layout.vertical.logicalOffset:revealOffset(layout.vertical.logicalOffset,layout.bodyHeight,cellTop,cellBottom,layout.vertical.logicalMaxOffset);const scrollLeft=logicalToPhysicalOffset(layout.horizontal,logicalLeft);const scrollTop=logicalToPhysicalOffset(layout.vertical,logicalTop);if(scrollLeft===this.#viewport.scrollLeft&&scrollTop===this.#viewport.scrollTop){return false}this.#viewport=Object.freeze({...this.#viewport,scrollLeft,scrollTop});return true}#scrollColumnIntoView(columnIndex){const layout=this.#snapshot.layout;const offsets=columnOffsets(layout.effectiveColumnWidths);const logicalLeft=columnIndex<layout.frozenColumnCount?layout.horizontal.logicalOffset:revealOffset(layout.horizontal.logicalOffset,layout.bodyWidth,offsets[columnIndex],offsets[columnIndex+1],layout.horizontal.logicalMaxOffset);const scrollLeft=logicalToPhysicalOffset(layout.horizontal,logicalLeft);if(scrollLeft===this.#viewport.scrollLeft){return false}this.#viewport=Object.freeze({...this.#viewport,scrollLeft});return true}#handleTableEvent(event){if(this.#disposed){return}switch(event.type){case"metadata":{const previousMetadata=this.#snapshot.metadata;this.#columnWidths=reconcileColumnWidths(previousMetadata,event.metadata,this.#columnWidths,this.#options);const hiddenById=new Map(previousMetadata.schema.columns.map((column,index)=>[column.id,this.#hiddenColumns[index]===true]));this.#hiddenColumns=event.metadata.schema.columns.map(column=>hiddenById.get(column.id)===true);this.#advanceGeneration();this.#clampInteraction();this.#publish();break}case"runtimeError":this.#requestAbort?.abort();this.#requestAbort=null;this.#loadingRanges=[];this.#rebuildSnapshot("error",event.error);break;case"closed":this.#tableClosed=true;this.#requestAbort?.abort();this.#requestAbort=null;this.#loadingRanges=[];if(this.#snapshot.status!=="error"){this.#rebuildSnapshot("closed")}break;case"warning":break}}#clampInteraction(){if(this.#activeCell===null){return}const rowCount=extentValue(this.#table.metadata.extent.rows);const columnCount=this.#table.metadata.schema.columns.length;const rawActive=clampCell(this.#activeCell,rowCount,columnCount);const nextActive=rawActive===null?null:this.#visibleCell(this.#mergeAnchor(rawActive),1,1);if(nextActive===null){this.#activeCell=null;this.#selection=null;return}this.#activeCell=nextActive;if(this.#selection!==null){const rawAnchor=clampCell(this.#selection.anchor,rowCount,columnCount);const rawFocus=clampCell(this.#selection.focus,rowCount,columnCount);const anchor=this.#visibleCell(this.#mergeAnchor(rawAnchor),1,1);const focus=this.#visibleCell(this.#mergeAnchor(rawFocus),1,1);this.#selection=anchor===null||focus===null?null:createMergedSelection(anchor,focus,this.#mergedCells)}}#layoutOptions(){return Object.freeze({...this.#options,rowEntries:this.#presentationRows,hiddenColumns:this.#hiddenColumns,frozenRows:this.#frozenRows,frozenColumns:this.#frozenColumns})}#mergeAnchor(cell){const region=mergeContaining(this.#mergedCells,cell);return region===void 0?Object.freeze({rowIndex:cell.rowIndex,columnIndex:cell.columnIndex}):Object.freeze({rowIndex:region.rowStart,columnIndex:region.columnStart})}#visibleCell(cell,rowDirection,columnDirection){const layout=this.#snapshot.layout;const row=nextVisibleAxisIndex(layout.rowGeometry,cell.rowIndex,rowDirection)??nextVisibleAxisIndex(layout.rowGeometry,cell.rowIndex,rowDirection===1?-1:1);if(row===null)return null;const column=nextVisibleColumn(layout.effectiveColumnWidths,cell.columnIndex,columnDirection)??nextVisibleColumn(layout.effectiveColumnWidths,cell.columnIndex,columnDirection===1?-1:1);return column===null?null:Object.freeze({rowIndex:row,columnIndex:column})}#normalizeInteractionForPresentation(){this.#clampInteraction()}#rebuildSnapshot(status,error){const layout=createTableLayout(this.#table.metadata,this.#columnWidths,this.#viewport,this.#layoutOptions());this.#viewport=Object.freeze({...this.#viewport,scrollLeft:layout.horizontal.physicalOffset,scrollTop:layout.vertical.physicalOffset});this.#snapshot=Object.freeze({generation:this.#generation,status,metadata:this.#table.metadata,layout,activeCell:this.#activeCell,selection:this.#selection,...error===void 0?{}:{error}});this.#emit()}#publish(){this.#snapshot=Object.freeze({...this.#snapshot,activeCell:this.#activeCell,selection:this.#selection});this.#emit()}#emit(){for(const listener of this.#listeners){try{listener(this.#snapshot)}catch(error){if(typeof reportError==="function"){reportError(error)}}}}#assertOpen(){if(this.#disposed||this.#tableClosed||this.#snapshot.status==="closed"){throw closedError("Table view controller")}}};function createTableController(table,options={}){if(!table||typeof table.readRange!=="function"||typeof table.subscribe!=="function"){throw invalidArgument("table must be a TableHandle")}return new Controller(table,options)}function normalizeMergedCells(regions,rowCount,columnCount){const unique=new Map;for(const region of regions){if(!region||!Number.isSafeInteger(region.rowStart)||!Number.isSafeInteger(region.rowEnd)||!Number.isSafeInteger(region.columnStart)||!Number.isSafeInteger(region.columnEnd)||region.rowStart<0||region.columnStart<0||region.rowEnd<=region.rowStart||region.columnEnd<=region.columnStart||region.rowEnd>rowCount||region.columnEnd>columnCount){continue}const frozen=Object.freeze({...region});unique.set(mergeKey(frozen),frozen)}return Object.freeze([...unique.values()].sort((left,right)=>left.rowStart-right.rowStart||left.columnStart-right.columnStart||left.rowEnd-right.rowEnd||left.columnEnd-right.columnEnd))}function sameMergedCells(left,right){return left.length===right.length&&left.every((region,index)=>mergeKey(region)===mergeKey(right[index]))}function mergeKey(region){return`${region.rowStart}:${region.rowEnd}:${region.columnStart}:${region.columnEnd}`}function mergeContaining(regions,cell){return regions.find(region=>cell.rowIndex>=region.rowStart&&cell.rowIndex<region.rowEnd&&cell.columnIndex>=region.columnStart&&cell.columnIndex<region.columnEnd)}function createMergedSelection(anchor,focus,regions){const selection=createSelection(anchor,focus);let range=selection.range;for(;;){let expanded=range;for(const region of regions){if(!rangesIntersect(expanded,region))continue;expanded=Object.freeze({rowStart:Math.min(expanded.rowStart,region.rowStart),rowEnd:Math.max(expanded.rowEnd,region.rowEnd),columnStart:Math.min(expanded.columnStart,region.columnStart),columnEnd:Math.max(expanded.columnEnd,region.columnEnd)})}if(sameGridRange(range,expanded))break;range=expanded}return Object.freeze({...selection,range})}function rangesIntersect(left,right){return left.rowStart<right.rowEnd&&left.rowEnd>right.rowStart&&left.columnStart<right.columnEnd&&left.columnEnd>right.columnStart}function navigationOrigin(current,command,regions){const merge=mergeContaining(regions,current);if(merge===void 0)return current;if(command==="right"){return Object.freeze({rowIndex:current.rowIndex,columnIndex:merge.columnEnd-1})}if(command==="down"||command==="page-down"){return Object.freeze({rowIndex:merge.rowEnd-1,columnIndex:current.columnIndex})}return current}function navigationDirections(command){switch(command){case"left":case"row-start":return[1,-1];case"up":case"page-up":return[-1,1];case"table-start":return[-1,-1];default:return[1,1]}}function nextVisibleColumn(widths,from,direction){for(let index=Math.min(widths.length-1,Math.max(0,from));index>=0&&index<widths.length;index+=direction){if((widths[index]??0)>0)return index}return null}function normalizeOptions(options){const minColumnWidth=positiveFinite2(options.minColumnWidth??DEFAULT_MIN_COLUMN_WIDTH,"minColumnWidth");const maxColumnWidth=positiveFinite2(options.maxColumnWidth??DEFAULT_MAX_COLUMN_WIDTH,"maxColumnWidth");if(maxColumnWidth<minColumnWidth){throw invalidArgument("maxColumnWidth must be greater than or equal to minColumnWidth")}const maxWindowCells=positiveInteger(options.maxWindowCells??DEFAULT_MAX_WINDOW_CELLS,"maxWindowCells");if(maxWindowCells>MAX_RANGE_CELLS){throw invalidArgument(`maxWindowCells cannot exceed ${MAX_RANGE_CELLS}`)}return Object.freeze({...options.rowHeight===void 0?{}:{rowHeight:options.rowHeight},...options.headerHeight===void 0?{}:{headerHeight:options.headerHeight},...options.rowHeaderWidth===void 0?{}:{rowHeaderWidth:options.rowHeaderWidth},...options.scrollPixelLimit===void 0?{}:{scrollPixelLimit:options.scrollPixelLimit},...options.overscanRows===void 0?{}:{overscanRows:options.overscanRows},...options.overscanColumns===void 0?{}:{overscanColumns:options.overscanColumns},columnWidth:positiveFinite2(options.columnWidth??DEFAULT_COLUMN_WIDTH,"columnWidth"),minColumnWidth,maxColumnWidth,columnWidths:Object.freeze({...options.columnWidths??{}}),maxWindowCells})}function initialColumnWidths(metadata,options){return metadata.schema.columns.map(column=>clampWidth(options.columnWidths[column.id]??options.columnWidth,options))}function reconcileColumnWidths(previousMetadata,nextMetadata,previousWidths,options){const byId=new Map(previousMetadata.schema.columns.map((column,index)=>[column.id,previousWidths[index]]));return nextMetadata.schema.columns.map(column=>clampWidth(byId.get(column.id)??options.columnWidths[column.id]??options.columnWidth,options))}function normalizeViewport(viewport){return Object.freeze({width:nonNegativeFinite2(viewport.width,"viewport.width"),height:nonNegativeFinite2(viewport.height,"viewport.height"),scrollLeft:nonNegativeFinite2(viewport.scrollLeft,"viewport.scrollLeft"),scrollTop:nonNegativeFinite2(viewport.scrollTop,"viewport.scrollTop"),devicePixelRatio:positiveFinite2(viewport.devicePixelRatio??1,"viewport.devicePixelRatio")})}function layoutRanges(layout,maxCells,mergedCells=[]){const visible=windowRectangles(layout.rows.visible,layout.columns.visible,layout.frozenRowRange,layout.frozenColumnRange);const anchors=mergeAnchorRanges(mergedCells,visible);const visibleWithAnchors=Object.freeze([...visible,...anchors.filter(anchor=>!visible.some(range=>rangeContainsRange(range,anchor)))]);const visibleCellCount=visibleWithAnchors.reduce((total,range)=>total+rangeCellCount(range),0);if(visibleCellCount>maxCells){throw new TabularkError("RESOURCE_LIMIT",`The viewport contains ${visibleCellCount} cells; the window limit is ${maxCells}`,{details:{resource:"viewport-cells",required:visibleCellCount,available:maxCells,visibleCellCount,maxCells}})}const fullOverscan=windowRectangles(layout.rows.overscan,layout.columns.overscan,layout.frozenRowRange,layout.frozenColumnRange);const overscanCellCount=fullOverscan.reduce((total,range)=>total+rangeCellCount(range),0);const overscanWithAnchors=Object.freeze([...fullOverscan,...anchors.filter(anchor=>!fullOverscan.some(range=>rangeContainsRange(range,anchor)))]);return Object.freeze({visible:visibleWithAnchors,overscan:overscanCellCount+anchors.length<=maxCells?overscanWithAnchors:visibleWithAnchors})}function mergeAnchorRanges(mergedCells,visible){const anchors=new Map;for(const region of mergedCells){if(!visible.some(range2=>rangesIntersect(range2,region)))continue;const range=Object.freeze({rowStart:region.rowStart,rowEnd:region.rowStart+1,columnStart:region.columnStart,columnEnd:region.columnStart+1});anchors.set(`${range.rowStart}:${range.columnStart}`,range)}return Object.freeze([...anchors.values()])}function windowRectangles(rows,columns,frozenRows,frozenColumns){const rowBands=mergeIndexBands(rows,frozenRows);const columnBands=mergeIndexBands(columns,frozenColumns);const rectangles=[];for(const row of rowBands){for(const column of columnBands){rectangles.push(Object.freeze({rowStart:row.start,rowEnd:row.end,columnStart:column.start,columnEnd:column.end}))}}return Object.freeze(rectangles)}function mergeIndexBands(primary,frozen){const bands=[primary,frozen].filter(range=>range.start<range.end).sort((left,right)=>left.start-right.start);if(bands.length<=1)return Object.freeze(bands.map(range=>Object.freeze({...range})));const[first,second]=bands;return first.end>=second.start?Object.freeze([Object.freeze({start:first.start,end:Math.max(first.end,second.end)})]):Object.freeze(bands.map(range=>Object.freeze({...range})))}function gridRangeToRequest(range){return Object.freeze({rowStart:range.rowStart,rowCount:range.rowEnd-range.rowStart,columnStart:range.columnStart,columnCount:range.columnEnd-range.columnStart})}function batchGridRange(batch){return Object.freeze({rowStart:batch.range.rowStart,rowEnd:batch.range.rowStart+batch.range.rowCount,columnStart:batch.range.columnStart,columnEnd:batch.range.columnStart+batch.range.columnCount})}function batchRows(batch){return Object.freeze(batch.toDisplayRows({maxCells:Math.max(1,batch.range.rowCount*batch.range.columnCount)}).map(row=>Object.freeze(row)))}function rowsToTsv(rows){return rows.map(row=>row.map(tsvField).join(" ")).join("\n")}function tsvField(value){if(value===null){return""}return/[\t\r\n"]/.test(value)?`"${value.replaceAll('"','""')}"`:value}function revealOffset(current,viewportSize,itemStart,itemEnd,maximum){if(viewportSize<=0||itemStart<current){return Math.min(maximum,Math.max(0,itemStart))}if(itemEnd>current+viewportSize){return Math.min(maximum,Math.max(0,itemEnd-viewportSize))}return current}function rangeContains(range,rowIndex,columnIndex){return rowIndex>=range.rowStart&&rowIndex<range.rowEnd&&columnIndex>=range.columnStart&&columnIndex<range.columnEnd}function rangeContainsRange(container,candidate){return candidate.rowStart>=container.rowStart&&candidate.rowEnd<=container.rowEnd&&candidate.columnStart>=container.columnStart&&candidate.columnEnd<=container.columnEnd}function rangeCellCount(range){return(range.rowEnd-range.rowStart)*(range.columnEnd-range.columnStart)}function sameGridRange(left,right){return right!==null&&left.rowStart===right.rowStart&&left.rowEnd===right.rowEnd&&left.columnStart===right.columnStart&&left.columnEnd===right.columnEnd}function sameViewport(left,right){return left.width===right.width&&left.height===right.height&&left.scrollLeft===right.scrollLeft&&left.scrollTop===right.scrollTop&&left.devicePixelRatio===right.devicePixelRatio}function isValidCell(rowIndex,columnIndex,rowCount,columnCount){return Number.isSafeInteger(rowIndex)&&rowIndex>=0&&rowIndex<rowCount&&Number.isSafeInteger(columnIndex)&&columnIndex>=0&&columnIndex<columnCount}function assertColumnIndex(columnIndex,columnCount){if(!Number.isSafeInteger(columnIndex)||columnIndex<0||columnIndex>=columnCount){throw invalidArgument(`columnIndex must be between 0 and ${Math.max(0,columnCount-1)}`)}}function assertWindowCellLimit(request,limit,label){const cells=request.rowCount*request.columnCount;if(!Number.isSafeInteger(cells)||cells>limit){throw new TabularkError("RESOURCE_LIMIT",`The ${label} contains ${cells} cells; the limit is ${limit}`,{details:{resource:"view-window-cells",required:cells,available:limit,cells,limit}})}}function clampWidth(width,options){if(!Number.isFinite(width)){throw invalidArgument("column width must be a finite number")}return Math.min(options.maxColumnWidth,Math.max(options.minColumnWidth,width))}function positiveFinite2(value,name){if(!Number.isFinite(value)||value<=0){throw invalidArgument(`${name} must be a positive finite number`)}return value}function nonNegativeFinite2(value,name){if(!Number.isFinite(value)||value<0){throw invalidArgument(`${name} must be a non-negative finite number`)}return value}function positiveInteger(value,name){if(!Number.isSafeInteger(value)||value<=0){throw invalidArgument(`${name} must be a positive safe integer`)}return value}export{CanvasTablePainter,DEFAULT_CANVAS_TABLE_THEME,DEFAULT_COLUMN_WIDTH,DEFAULT_HEADER_HEIGHT,DEFAULT_MAX_COLUMN_WIDTH,DEFAULT_MIN_COLUMN_WIDTH,DEFAULT_OVERSCAN_COLUMNS,DEFAULT_OVERSCAN_ROWS,DEFAULT_ROW_HEADER_WIDTH,DEFAULT_ROW_HEIGHT,DEFAULT_SCROLL_PIXEL_LIMIT,axisIndexAtOffset,axisPosition,axisSize,cellRect,clampCell,columnHeaderRect,containsCell,createScrollAxis,createSelection,createSparseAxisGeometry,createTableController,createTableLayout,hitTest,logicalToPhysicalOffset,moveCell,nextVisibleAxisIndex,physicalToLogicalOffset,rowHeaderRect,selectionRange,selectionRect};
2
2
  //# sourceMappingURL=experimental.js.map