Skip to content

feat: support tri-state column sorting#501

Merged
zknpr merged 1 commit into
zknpr:devfrom
yukina3230:feat/unsorted-state
Jun 21, 2026
Merged

feat: support tri-state column sorting#501
zknpr merged 1 commit into
zknpr:devfrom
yukina3230:feat/unsorted-state

Conversation

@yukina3230

@yukina3230 yukina3230 commented Jun 21, 2026

Copy link
Copy Markdown

Description

  • Cycles column sort states through: none -> ascending -> descending -> none.
  • Clears sortedColumn on "none" to omit ORDER BY and restore original row order.
  • Moves the sort arrow (▲/▼) outside .header-text into a dedicated flex item.
  • Sets flex-shrink: 0 on the arrow to prevent clipping on narrow/truncated columns.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that changes existing behavior)
  • Documentation update
  • Refactor / chore (no functional change)

Checklist

  • npm test passes locally
  • npm run build completes without errors
  • I followed the project's coding standards (parameterized SQL, escaped identifiers, textContent rendering, strict CSP)
  • I added or updated tests for my changes
  • I updated the documentation (README / CHANGELOG / CLAUDE.md) where relevant
  • My commits follow Conventional Commits

Screenshots

tri_state.webm

@vercel

vercel Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

@yukina3230 is attempting to deploy a commit to the zknpr's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5f6e7157-4ac8-4e28-ba90-7a8eea5b653f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a three-state column sorting cycle (none -> ascending -> descending -> none) on repeated clicks of the same column. It also refactors the HTML structure and CSS of the sort indicator to prevent it from being clipped by text-overflow ellipsis on narrow columns by rendering it as its own flex item with flex-shrink: 0. There are no review comments, so I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@zknpr

zknpr commented Jun 21, 2026

Copy link
Copy Markdown
Owner

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 172b960890

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -364,45 +364,45 @@
</div>

<script nonce="<!--NONCE-->">
"use strict";(()=>{var he=window.parent;var Pt=6e4,Dt=0,G=new Map;function ge(){return window.location.ancestorOrigins?.[0]||"*"}async function Ne(e){if(e.length<=65536)return Nt(e);let o=32768,l=[];for(let a=0;a<e.length;a+=o){let s=e.subarray(a,Math.min(a+o,e.length));l.push(String.fromCharCode.apply(null,s)),a>0&&a/o%4===0&&await new Promise(i=>setTimeout(i,0))}return btoa(l.join(""))}function Nt(e){let o=[];for(let l=0;l<e.length;l+=32768){let a=e.subarray(l,Math.min(l+32768,e.length));o.push(String.fromCharCode.apply(null,a))}return btoa(o.join(""))}function kt(e){let t=atob(e),o=new Uint8Array(t.length);for(let l=0;l<t.length;l++)o[l]=t.charCodeAt(l);return o}async function pe(e){if(e instanceof Uint8Array)return{__type:"Uint8Array",base64:await Ne(e)};if(ArrayBuffer.isView(e)){let t=new Uint8Array(e.buffer,e.byteOffset,e.byteLength);return{__type:"Uint8Array",base64:await Ne(t)}}if(Array.isArray(e))return Promise.all(e.map(pe));if(e&&typeof e=="object"&&Object.prototype.toString.call(e)==="[object Object]"){let t={};for(let o of Object.keys(e))t[o]=await pe(e[o]);return t}return e}async function At(e){return Promise.all(e.map(pe))}function fe(e){if(e&&typeof e=="object"&&!Array.isArray(e)){let t=Object.keys(e);if(e.__type==="Uint8Array"&&typeof e.base64=="string"&&t.length===2&&t.includes("__type")&&t.includes("base64"))return kt(e.base64);if(e.__type==="Uint8Array"&&Array.isArray(e.data)&&t.length===2&&t.includes("__type")&&t.includes("data"))return new Uint8Array(e.data);let o={};for(let l of Object.keys(e))o[l]=fe(e[l]);return o}return Array.isArray(e)?e.map(fe):e}async function E(e,t){let o=`rpc_${++Dt}_${Date.now()}`,l=await At(t);return new Promise((a,s)=>{let i=setTimeout(()=>{G.has(o)&&(G.delete(o),s(new Error(`RPC timeout: ${e}`)))},Pt);G.set(o,{resolve:a,reject:s,timeoutId:i}),he.postMessage({channel:"rpc",content:{kind:"invoke",messageId:o,targetMethod:e,payload:l}},ge())})}function ke(e){if(!e||e.kind!=="response")return;let t=G.get(e.messageId);if(t)if(clearTimeout(t.timeoutId),G.delete(e.messageId),e.success){let o=fe(e.data);t.resolve(o)}else t.reject(new Error(e.errorMessage||"RPC failed"))}function Ae(e,t){he.postMessage({kind:"result",correlationId:e,payload:t},ge())}function ye(e,t){he.postMessage({kind:"result",correlationId:e,errorText:t},ge())}var y={initialize:()=>E("initialize",[]),exportDb:e=>E("exportDb",[e]),refreshFile:()=>E("refreshFile",[]),fireEditEvent:e=>E("fireEditEvent",[e]),exportTable:(e,t,o,l,a,s)=>E("exportTable",[e,t,o,l,a,s]),updateCell:(e,t,o,l,a)=>E("updateCell",[e,t,o,l,a]),insertRow:(e,t)=>E("insertRow",[e,t]),deleteRows:(e,t)=>E("deleteRows",[e,t]),deleteColumns:(e,t)=>E("deleteColumns",[e,t]),createTable:(e,t)=>E("createTable",[e,t]),updateCellBatch:(e,t,o)=>E("updateCellBatch",[e,t,o]),addColumn:(e,t,o,l)=>E("addColumn",[e,t,o,l]),fetchTableData:(e,t)=>E("fetchTableData",[e,t]),fetchTableCount:(e,t)=>E("fetchTableCount",[e,t]),fetchSchema:()=>E("fetchSchema",[]),getTableInfo:e=>E("getTableInfo",[e]),getPragmas:()=>E("getPragmas",[]),setPragma:(e,t)=>E("setPragma",[e,t]),getExtensionSettings:()=>E("getExtensionSettings",[]),updateExtensionSetting:(e,t)=>E("updateExtensionSetting",[e,t]),ping:()=>E("ping",[]),openCellEditor:()=>Promise.resolve({success:!1,message:"Not available in web mode"}),readWorkspaceFileUri:()=>Promise.resolve(null),triggerUndo:()=>Promise.resolve(),triggerRedo:()=>Promise.resolve(),saveFile:(e,t)=>{let o=new Blob([t]),l=URL.createObjectURL(o),a=document.createElement("a");return a.href=l,a.download=e,document.body.appendChild(a),a.click(),document.body.removeChild(a),setTimeout(()=>URL.revokeObjectURL(l),100),Promise.resolve()},selectFile:()=>new Promise(e=>{let t=document.createElement("input");t.type="file",t.style.display="none",t.onchange=async o=>{if(o.target.files.length>0){let l=o.target.files[0],a=await l.arrayBuffer();e({name:l.name,data:new Uint8Array(a)})}else e(void 0)},document.body.appendChild(t),t.click(),setTimeout(()=>document.body.removeChild(t),1e3)})};var n={isDbConnected:!1,selectedTable:null,selectedTableType:"table",currentPageIndex:0,rowsPerPage:500,totalRecordCount:0,totalPageCount:1,tableColumns:[],sortedColumn:null,sortAscending:!0,filterQuery:"",filterTimer:null,selectedRowIds:new Set,gridData:[],editingCellInfo:null,activeCellInput:null,isSavingCell:!1,isLoadingData:!1,lastDoubleClickTime:0,isTransitioningEdit:!1,transitionLockTimeout:null,selectedCells:[],lastSelectedCell:null,lastSelectedColumnIndex:null,lastSelectedRowIndex:null,columnWidths:{},resizingColumn:null,resizeStartX:0,resizeStartWidth:0,columnFilters:{},pinnedColumns:new Set,pinnedRowIds:new Set,cellPreviewInfo:null,cellPreviewWrapEnabled:!0,selectedColumns:new Set,scrollPosition:{top:0,left:0},schemaCache:{tables:[],views:[],indexes:[]},sidebarFilter:"",dateFormat:"raw",cellEditBehavior:"inline"},Ce;function $(){Ce&&clearTimeout(Ce),Ce=setTimeout(()=>{n.selectedTable,n.selectedTableType,n.currentPageIndex,n.rowsPerPage,n.sortedColumn,n.sortAscending,n.filterQuery,n.columnWidths,n.columnFilters,Array.from(n.pinnedColumns),Array.from(n.pinnedRowIds),Array.from(n.selectedColumns),n.sidebarFilter,n.scrollPosition,n.dateFormat,n.cellEditBehavior},500)}function J(e){return e==null?"":String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function te(e){let t=Number(e);if(!Number.isFinite(t))throw new Error(`Invalid rowid: ${e}`);return t}function _(e,t=null,o="raw",l=null){if(e==null)return"NULL";if(e instanceof Uint8Array)return"[BLOB]";if(o!=="raw"&&$t(t,l)){let s=Mt(e,o);if(s)return s}return typeof e=="string"&&e.length>100?e.substring(0,100)+"...":String(e)}function $t(e,t){if(e){let o=e.toUpperCase();if(o.includes("DATE")||o.includes("TIME")||o.includes("TIMESTAMP"))return!0}if(t){let o=t.toUpperCase();return o.endsWith("_AT")||o.endsWith("_ON")||o.includes("DATE")||o.includes("TIME")||o==="CREATED"||o==="UPDATED"}return!1}function Mt(e,t){if(!e)return null;let o;if(e instanceof Date)o=e;else if(typeof e=="number")e<1e11?o=new Date(e*1e3):o=new Date(e);else{let l=String(e);/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/.test(l)&&(l=l.replace(" ","T"));let a=Date.parse(l);if(isNaN(a))return null;o=new Date(a)}if(isNaN(o.getTime()))return null;switch(t){case"local":return o.toLocaleString();case"iso":return o.toISOString();case"relative":return Ft(o);default:return String(e)}}function Ft(e){let t=Math.floor((new Date-e)/1e3),o={year:31536e3,month:2592e3,week:604800,day:86400,hour:3600,minute:60,second:1},l;for(let[a,s]of Object.entries(o))if(l=Math.floor(t/s),l>0)return l===1?`1 ${a} ago`:`${l} ${a}s ago`;return"Just now"}function u(e){let t=document.getElementById("statusText");t&&(t.textContent=e)}function $e(){let e=document.getElementById("gridContainer");e&&(e.innerHTML=`
"use strict";(()=>{var ne=typeof acquireVsCodeApi<"u"?acquireVsCodeApi():null;function Fe(e){ne&&ne.setState(e)}var Ut=6e4,Ft=0,ge=new Map;async function Ue(e){if(e.length<=65536)return Ot(e);let o=32768,l=[];for(let a=0;a<e.length;a+=o){let s=e.subarray(a,Math.min(a+o,e.length));l.push(String.fromCharCode.apply(null,s)),a>0&&a/o%4===0&&await new Promise(r=>setTimeout(r,0))}return btoa(l.join(""))}function Ot(e){let o=[];for(let l=0;l<e.length;l+=32768){let a=e.subarray(l,Math.min(l+32768,e.length));o.push(String.fromCharCode.apply(null,a))}return btoa(o.join(""))}async function he(e){if(e instanceof Uint8Array)return{__type:"Uint8Array",base64:await Ue(e)};if(ArrayBuffer.isView(e)){let t=new Uint8Array(e.buffer,e.byteOffset,e.byteLength);return{__type:"Uint8Array",base64:await Ue(t)}}if(Array.isArray(e))return Promise.all(e.map(he));if(e&&typeof e=="object"&&Object.prototype.toString.call(e)==="[object Object]"){let t={};for(let o of Object.keys(e))t[o]=await he(e[o]);return t}return e}async function zt(e){return Promise.all(e.map(he))}async function E(e,t){let o=`rpc_${++Ft}_${Date.now()}`,l=await zt(t);return new Promise((a,s)=>{let r=setTimeout(()=>{ge.has(o)&&(ge.delete(o),s(new Error(`RPC timeout: ${e}`)))},Ut);ge.set(o,{resolve:a,reject:s,timeoutId:r}),ne?ne.postMessage({channel:"rpc",content:{kind:"invoke",messageId:o,targetMethod:e,payload:l}}):console.warn("VS Code API not available")})}var C={initialize:()=>E("initialize",[]),exportDb:e=>E("exportDb",[e]),refreshFile:()=>E("refreshFile",[]),fireEditEvent:e=>E("fireEditEvent",[e]),exportTable:(e,t,o,l,a,s)=>E("exportTable",[e,t,o,l,a,s]),updateCell:(e,t,o,l,a)=>E("updateCell",[e,t,o,l,a]),insertRow:(e,t)=>E("insertRow",[e,t]),deleteRows:(e,t)=>E("deleteRows",[e,t]),deleteColumns:(e,t)=>E("deleteColumns",[e,t]),createTable:(e,t)=>E("createTable",[e,t]),updateCellBatch:(e,t,o)=>E("updateCellBatch",[e,t,o]),addColumn:(e,t,o,l)=>E("addColumn",[e,t,o,l]),fetchTableData:(e,t)=>E("fetchTableData",[e,t]),fetchTableCount:(e,t)=>E("fetchTableCount",[e,t]),fetchSchema:()=>E("fetchSchema",[]),getTableInfo:e=>E("getTableInfo",[e]),getPragmas:()=>E("getPragmas",[]),setPragma:(e,t)=>E("setPragma",[e,t]),getExtensionSettings:()=>E("getExtensionSettings",[]),updateExtensionSetting:(e,t)=>E("updateExtensionSetting",[e,t]),ping:()=>E("ping",[]),openCellEditor:(e,t,o,l,a)=>E("openCellEditor",[e,t,o,l,a]),readWorkspaceFileUri:e=>E("readWorkspaceFileUri",[e]),saveFile:(e,t)=>E("saveFile",[e,t]),selectFile:()=>E("selectFile",[]),triggerUndo:()=>E("triggerUndo",[]),triggerRedo:()=>E("triggerRedo",[])};var n={isDbConnected:!1,selectedTable:null,selectedTableType:"table",currentPageIndex:0,rowsPerPage:500,totalRecordCount:0,totalPageCount:1,tableColumns:[],sortedColumn:null,sortAscending:!0,filterQuery:"",filterTimer:null,selectedRowIds:new Set,gridData:[],editingCellInfo:null,activeCellInput:null,isSavingCell:!1,isLoadingData:!1,lastDoubleClickTime:0,isTransitioningEdit:!1,transitionLockTimeout:null,selectedCells:[],lastSelectedCell:null,lastSelectedColumnIndex:null,lastSelectedRowIndex:null,columnWidths:{},resizingColumn:null,resizeStartX:0,resizeStartWidth:0,columnFilters:{},pinnedColumns:new Set,pinnedRowIds:new Set,cellPreviewInfo:null,cellPreviewWrapEnabled:!0,selectedColumns:new Set,scrollPosition:{top:0,left:0},schemaCache:{tables:[],views:[],indexes:[]},sidebarFilter:"",dateFormat:"raw",cellEditBehavior:"inline"},ye;function M(){ye&&clearTimeout(ye),ye=setTimeout(()=>{Fe({selectedTable:n.selectedTable,selectedTableType:n.selectedTableType,currentPageIndex:n.currentPageIndex,rowsPerPage:n.rowsPerPage,sortedColumn:n.sortedColumn,sortAscending:n.sortAscending,filterQuery:n.filterQuery,columnWidths:n.columnWidths,columnFilters:n.columnFilters,pinnedColumns:Array.from(n.pinnedColumns),pinnedRowIds:Array.from(n.pinnedRowIds),selectedColumns:Array.from(n.selectedColumns),sidebarFilter:n.sidebarFilter,scrollPosition:n.scrollPosition,dateFormat:n.dateFormat,cellEditBehavior:n.cellEditBehavior})},500)}var be=window.parent;var jt=6e4,Vt=0,J=new Map;function we(){return window.location.ancestorOrigins?.[0]||"*"}async function Oe(e){if(e.length<=65536)return _t(e);let o=32768,l=[];for(let a=0;a<e.length;a+=o){let s=e.subarray(a,Math.min(a+o,e.length));l.push(String.fromCharCode.apply(null,s)),a>0&&a/o%4===0&&await new Promise(r=>setTimeout(r,0))}return btoa(l.join(""))}function _t(e){let o=[];for(let l=0;l<e.length;l+=32768){let a=e.subarray(l,Math.min(l+32768,e.length));o.push(String.fromCharCode.apply(null,a))}return btoa(o.join(""))}function Wt(e){let t=atob(e),o=new Uint8Array(t.length);for(let l=0;l<t.length;l++)o[l]=t.charCodeAt(l);return o}async function Ce(e){if(e instanceof Uint8Array)return{__type:"Uint8Array",base64:await Oe(e)};if(ArrayBuffer.isView(e)){let t=new Uint8Array(e.buffer,e.byteOffset,e.byteLength);return{__type:"Uint8Array",base64:await Oe(t)}}if(Array.isArray(e))return Promise.all(e.map(Ce));if(e&&typeof e=="object"&&Object.prototype.toString.call(e)==="[object Object]"){let t={};for(let o of Object.keys(e))t[o]=await Ce(e[o]);return t}return e}async function Kt(e){return Promise.all(e.map(Ce))}function xe(e){if(e&&typeof e=="object"&&!Array.isArray(e)){let t=Object.keys(e);if(e.__type==="Uint8Array"&&typeof e.base64=="string"&&t.length===2&&t.includes("__type")&&t.includes("base64"))return Wt(e.base64);if(e.__type==="Uint8Array"&&Array.isArray(e.data)&&t.length===2&&t.includes("__type")&&t.includes("data"))return new Uint8Array(e.data);let o={};for(let l of Object.keys(e))o[l]=xe(e[l]);return o}return Array.isArray(e)?e.map(xe):e}async function I(e,t){let o=`rpc_${++Vt}_${Date.now()}`,l=await Kt(t);return new Promise((a,s)=>{let r=setTimeout(()=>{J.has(o)&&(J.delete(o),s(new Error(`RPC timeout: ${e}`)))},jt);J.set(o,{resolve:a,reject:s,timeoutId:r}),be.postMessage({channel:"rpc",content:{kind:"invoke",messageId:o,targetMethod:e,payload:l}},we())})}function ze(e){if(!e||e.kind!=="response")return;let t=J.get(e.messageId);if(t)if(clearTimeout(t.timeoutId),J.delete(e.messageId),e.success){let o=xe(e.data);t.resolve(o)}else t.reject(new Error(e.errorMessage||"RPC failed"))}function je(e,t){be.postMessage({kind:"result",correlationId:e,payload:t},we())}function Ee(e,t){be.postMessage({kind:"result",correlationId:e,errorText:t},we())}var Ie={initialize:()=>I("initialize",[]),exportDb:e=>I("exportDb",[e]),refreshFile:()=>I("refreshFile",[]),fireEditEvent:e=>I("fireEditEvent",[e]),exportTable:(e,t,o,l,a,s)=>I("exportTable",[e,t,o,l,a,s]),updateCell:(e,t,o,l,a)=>I("updateCell",[e,t,o,l,a]),insertRow:(e,t)=>I("insertRow",[e,t]),deleteRows:(e,t)=>I("deleteRows",[e,t]),deleteColumns:(e,t)=>I("deleteColumns",[e,t]),createTable:(e,t)=>I("createTable",[e,t]),updateCellBatch:(e,t,o)=>I("updateCellBatch",[e,t,o]),addColumn:(e,t,o,l)=>I("addColumn",[e,t,o,l]),fetchTableData:(e,t)=>I("fetchTableData",[e,t]),fetchTableCount:(e,t)=>I("fetchTableCount",[e,t]),fetchSchema:()=>I("fetchSchema",[]),getTableInfo:e=>I("getTableInfo",[e]),getPragmas:()=>I("getPragmas",[]),setPragma:(e,t)=>I("setPragma",[e,t]),getExtensionSettings:()=>I("getExtensionSettings",[]),updateExtensionSetting:(e,t)=>I("updateExtensionSetting",[e,t]),ping:()=>I("ping",[]),openCellEditor:()=>Promise.resolve({success:!1,message:"Not available in web mode"}),readWorkspaceFileUri:()=>Promise.resolve(null),triggerUndo:()=>Promise.resolve(),triggerRedo:()=>Promise.resolve(),saveFile:(e,t)=>{let o=new Blob([t]),l=URL.createObjectURL(o),a=document.createElement("a");return a.href=l,a.download=e,document.body.appendChild(a),a.click(),document.body.removeChild(a),setTimeout(()=>URL.revokeObjectURL(l),100),Promise.resolve()},selectFile:()=>new Promise(e=>{let t=document.createElement("input");t.type="file",t.style.display="none",t.onchange=async o=>{if(o.target.files.length>0){let l=o.target.files[0],a=await l.arrayBuffer();e({name:l.name,data:new Uint8Array(a)})}else e(void 0)},document.body.appendChild(t),t.click(),setTimeout(()=>document.body.removeChild(t),1e3)})};function X(e){return e==null?"":String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function oe(e){let t=Number(e);if(!Number.isFinite(t))throw new Error(`Invalid rowid: ${e}`);return t}function K(e,t=null,o="raw",l=null){if(e==null)return"NULL";if(e instanceof Uint8Array)return"[BLOB]";if(o!=="raw"&&qt(t,l)){let s=Ht(e,o);if(s)return s}return typeof e=="string"&&e.length>100?e.substring(0,100)+"...":String(e)}function qt(e,t){if(e){let o=e.toUpperCase();if(o.includes("DATE")||o.includes("TIME")||o.includes("TIMESTAMP"))return!0}if(t){let o=t.toUpperCase();return o.endsWith("_AT")||o.endsWith("_ON")||o.includes("DATE")||o.includes("TIME")||o==="CREATED"||o==="UPDATED"}return!1}function Ht(e,t){if(!e)return null;let o;if(e instanceof Date)o=e;else if(typeof e=="number")e<1e11?o=new Date(e*1e3):o=new Date(e);else{let l=String(e);/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/.test(l)&&(l=l.replace(" ","T"));let a=Date.parse(l);if(isNaN(a))return null;o=new Date(a)}if(isNaN(o.getTime()))return null;switch(t){case"local":return o.toLocaleString();case"iso":return o.toISOString();case"relative":return Gt(o);default:return String(e)}}function Gt(e){let t=Math.floor((new Date-e)/1e3),o={year:31536e3,month:2592e3,week:604800,day:86400,hour:3600,minute:60,second:1},l;for(let[a,s]of Object.entries(o))if(l=Math.floor(t/s),l>0)return l===1?`1 ${a} ago`:`${l} ${a}s ago`;return"Just now"}function u(e){let t=document.getElementById("statusText");t&&(t.textContent=e)}function Ve(){let e=document.getElementById("gridContainer");e&&(e.innerHTML=`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the web demo on the parent-window RPC client

This generated web-demo bundle now includes both the VS Code RPC client (acquireVsCodeApi/C) and the parent-window client (Ie), but the bundled schema/data paths call C.fetchSchema, C.getTableInfo, and C.fetchTableData. In the website iframe acquireVsCodeApi is undefined, so those calls only log VS Code API not available and then hang until timeout instead of posting to window.parent; selecting any table in the public demo will never load data.

Useful? React with 👍 / 👎.

@zknpr zknpr changed the base branch from main to dev June 21, 2026 19:55
@zknpr zknpr force-pushed the feat/unsorted-state branch from 172b960 to 0e88ee1 Compare June 21, 2026 20:46
@zknpr zknpr merged commit 4d50631 into zknpr:dev Jun 21, 2026
1 of 2 checks passed
zknpr pushed a commit that referenced this pull request Jun 21, 2026
Press Enter in the global or a column filter to jump between cells whose displayed text contains the active term, cycling with Enter/Shift+Enter, a current/total counter, and an outlined active-match cell. Initial Enter applies the filter (one fetch); subsequent presses cycle locally without re-querying. Match navigation resets on sort/page/page-size/filter-text/date-format changes.

Review hardening (Gemini + 4 Codex rounds): fixed the btnApplyFilter MouseEvent-as-direction crash; guarded the toolbar filter against concurrent reloads (state.isGridReloading); preventDefault + IME (isComposing) handling on Enter; String() around formatter output in match scan; failed/superseded filter-submit lifecycle (loadTableData returns success; only a fully-applied load navigates, failures revert for retry); pinned active-match z-index; removed dead filterTimer. Composes with #498/#499/#501. tsc + 454 unit tests green.

Co-authored-by: yukina3230 <75545944+yukina3230@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@zknpr

zknpr commented Jun 21, 2026

Copy link
Copy Markdown
Owner

Thanks for this, @yukina3230! 🙏 Clean take on the none → asc → desc → none cycle, and I really like moving the sort arrow out of the header-text so it can't get clipped by the ellipsis on narrow columns — nice detail. Merged into dev for the next release. Appreciate it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants