@aiquants/spreadsheet 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
1
  /*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
2
2
  /*! @aiquants/spreadsheet standalone CSS — 非 Tailwind ホスト専用。ホストの Tailwind ビルドと混在させないこと */
3
- @layer theme,base;@layer components{@layer components{.aqvs-scroll-pane{display:flex;position:relative}.aqvs-scroll-pane[data-self-measured=true]{height:100%}.aqvs-scroll-pane-content{box-sizing:border-box;flex:1;height:100%;position:relative;overflow:hidden}.aqvs-tap-scroll-circle{touch-action:none;-webkit-user-select:none;user-select:none;justify-content:center;align-items:center;transition-property:transform;transition-duration:.1s;transition-timing-function:cubic-bezier(0,0,.2,1);display:flex;position:relative}.aqvs-tap-scroll-circle-visual-outer{border-radius:9999px;position:absolute;inset:0}.aqvs-tap-scroll-circle-gradient{background:linear-gradient(to bottom right,#1d4ed899,#60a5fa8c,#bfdbfe66);border:1px solid #fff6;border-radius:9999px;position:absolute;inset:0;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.aqvs-tap-scroll-circle-inner{border-radius:9999px;position:absolute;inset:18%}.aqvs-sample-visual-pupil{box-sizing:border-box;background-color:#ffffffd9;border:1px solid #ffffff80;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-sample-visual-highlight{background-color:#fffc;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-sample-visual-rod{background-color:#ffffff80;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-scrollbar{z-index:50;cursor:default;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;touch-action:none;background-color:#fff;position:relative}.aqvs-scrollbar-horizontal{flex-direction:row;align-items:stretch;display:flex}.aqvs-scrollbar-vertical{flex-direction:column;align-items:stretch;display:flex}.aqvs-scrollbar[data-visible=false],.aqvs-scrollbar-thumb-wrapper[data-visible=false]{opacity:0;pointer-events:none}.aqvs-scrollbar-tap-circle-wrapper{pointer-events:auto;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);position:absolute}.aqvs-scrollbar-arrow-button{color:#313131;letter-spacing:normal;background-color:#e0e0e0;border:0 solid;justify-content:center;align-items:center;padding:0;font-family:system-ui,sans-serif;font-size:.75rem;font-weight:400;line-height:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);display:flex}.aqvs-scrollbar-arrow-button:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scrollbar-arrow-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.aqvs-scrollbar-arrow-button:disabled{cursor:not-allowed;opacity:.5}.aqvs-scrollbar-arrow-button:enabled:hover{background-color:#d4d4d4}.aqvs-scrollbar-track{background-color:#f5f5f5;flex:1;position:relative}.aqvs-scrollbar-overlay{pointer-events:none;position:absolute;inset:0}.aqvs-scrollbar-thumb-wrapper{touch-action:none;position:absolute}.aqvs-scrollbar-thumb{transform-origin:50%;background-color:#7f7f7f;transition:transform 80ms ease-out;position:absolute}.aqvs-scrollbar-thumb[data-thumb-state=disabled]{transition:none;transform:none}.aqvs-scrollbar-thumb[data-thumb-state=hover]{background-color:#5f5f5f}.aqvs-scrollbar-thumb[data-thumb-state=dragging]{background-color:#4f4f4f;transition:transform 60ms ease-out}.aqvs-scrollbar-thumb-horizontal{inset:1.5px 0}.aqvs-scrollbar-thumb-horizontal[data-thumb-state=hover]{top:-.5px;bottom:-.5px;transform:scaleY(1.06)}.aqvs-scrollbar-thumb-horizontal[data-thumb-state=dragging]{top:-.5px;bottom:-.5px;transform:scaleY(1.12)}.aqvs-scrollbar-thumb-vertical{inset:0 1.5px}.aqvs-scrollbar-thumb-vertical[data-thumb-state=hover]{left:-.5px;right:-.5px;transform:scaleX(1.06)}.aqvs-scrollbar-thumb-vertical[data-thumb-state=dragging]{left:-.5px;right:-.5px;transform:scaleX(1.12)}.aqvs-scroll-to-edge-button{pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;color:#fff;text-transform:uppercase;letter-spacing:.05em;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background-color:#1f2937cc;border:0 solid;border-radius:9999px;padding:.25rem 3rem;font-family:inherit;font-size:10px;font-weight:500;line-height:1.5;transition-property:transform,background-color;transition-duration:.15s;transform:none;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.aqvs-scroll-to-edge-button:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scroll-to-edge-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.aqvs-scroll-to-edge-button:hover{background-color:#374151}.aqvs-scroll-to-edge-button:active{transform:scale(.95)}.aqvs-scroll-to-edge-overlay{pointer-events:none;z-index:10;transition-property:opacity;transition-duration:.5s;position:absolute;inset:0}.aqvs-scroll-to-edge-overlay[data-visible=true]{opacity:1}.aqvs-scroll-to-edge-overlay[data-visible=false]{opacity:0}.aqvs-scroll-to-edge-overlay[data-visible=false] .aqvs-scroll-to-edge-button{pointer-events:none}.aqvs-scroll-to-edge-button-container{justify-content:center;display:flex;position:absolute;left:0;right:0}.aqvs-scroll-to-edge-button-container-top{top:.5rem}.aqvs-scroll-to-edge-button-container-bottom{bottom:.5rem}.aqvs-no-items-container{width:100%;position:absolute;top:0}.aqvs-no-items-text{text-align:center;color:#6b7280}.aqvs-item-container,.aqvs-bottom-inset,.aqvs-items-wrapper{width:100%;position:absolute}.aqvs-live-region{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.aqvs-grid{grid-template-rows:minmax(0,1fr) auto;width:100%;height:100%;display:grid;position:relative;overflow:hidden}.aqvs-grid-has-frozen-rows{grid-template-rows:auto minmax(0,1fr) auto}.aqvs-grid-has-trailing-rows{grid-template-rows:minmax(0,1fr) auto auto}.aqvs-grid-has-frozen-rows.aqvs-grid-has-trailing-rows{grid-template-rows:auto minmax(0,1fr) auto auto}.aqvs-grid-frozen-rows{min-width:0;position:relative;overflow:hidden}.aqvs-grid-frozen-band-row{position:absolute;left:0;overflow:hidden}.aqvs-grid-trailing-rows{min-width:0;position:relative;overflow:hidden}.aqvs-grid-trailing-rows-inner{position:absolute;bottom:0;left:0;right:0}.aqvs-grid-trailing-band-row{position:absolute;left:0;overflow:hidden}.aqvs-grid-main{min-width:0;min-height:0;position:relative}.aqvs-grid-row{width:100%;height:100%;transform:translateX(var(--aqvs-grid-hx-residual,0px));position:relative;overflow:visible}.aqvs-grid-cell{height:100%;position:absolute;top:0;overflow:hidden}.aqvs-grid-row.aqvs-grid-row-frozen-host{transform:none}.aqvs-grid-row-scroll{height:100%;top:0;left:var(--aqvs-grid-frozen-width,0px);right:var(--aqvs-grid-trailing-width,0px);position:absolute;overflow:hidden}.aqvs-grid-row-scroll-inner{width:100%;height:100%;transform:translateX(calc(var(--aqvs-grid-hx-residual,0px) - var(--aqvs-grid-frozen-width,0px)));position:absolute;top:0;left:0}.aqvs-grid-row-trailing{height:100%;top:0;left:max(var(--aqvs-grid-frozen-width,0px), calc(100% - var(--aqvs-grid-trailing-width,0px)));position:absolute;right:0;overflow:hidden}.aqvs-grid-row-trailing-inner{height:100%;width:var(--aqvs-grid-trailing-width,0px);position:absolute;top:0;right:0}.aqvs-grid-hbar-strip{min-width:0;padding-left:var(--aqvs-grid-frozen-width,0px);position:relative}.aqvs-grid-live-region{clip:rect(0 0 0 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}}.aqsp-root,.aqsp-root *,.aqsp-root :before,.aqsp-root :after{box-sizing:border-box}.aqsp-root{--aqsp-row-header-width:64px;--aqsp-col-header-height:24px;--aqsp-band-row:#0084ff;--aqsp-band-column:#42ff4f;--aqsp-band-intersection:#e1ff00;--aqsp-band-opacity:.2;color:#1f2937;background:#fff;width:100%;height:100%;font-size:13px;line-height:1;position:relative;overflow:hidden}.aqsp-zoom{grid-template-columns:var(--aqsp-row-header-width) minmax(0, 1fr);grid-template-rows:var(--aqsp-col-header-height) minmax(0, 1fr);transform-origin:0 0;width:100%;height:100%;display:grid}.aqsp-corner{background:#f3f4f6;border-bottom:1px solid #d1d5db;border-right:1px solid #d1d5db;grid-area:1/1}.aqsp-col-header{-webkit-user-select:none;user-select:none;background:#f3f4f6;border-bottom:1px solid #d1d5db;grid-area:1/2;position:relative;overflow:hidden}.aqsp-col-header-scroll{height:100%;position:absolute;top:0;right:0;overflow:hidden}.aqsp-row-header-scroll{width:100%;position:absolute;bottom:0;left:0;overflow:hidden}.aqsp-col-header-track{will-change:transform;position:absolute;inset:0}.aqsp-col-header-cell{white-space:nowrap;color:#4b5563;border-right:1px solid #e5e7eb;justify-content:center;align-items:center;height:100%;display:flex;position:absolute;top:0;overflow:hidden}.aqsp-row-header{-webkit-user-select:none;user-select:none;background:#f3f4f6;border-right:1px solid #d1d5db;grid-area:2/1;position:relative;overflow:hidden}.aqsp-row-header-track{will-change:transform;position:absolute;top:0;left:0;right:0}.aqsp-row-header-cell{white-space:nowrap;color:#4b5563;font-variant-numeric:tabular-nums;border-bottom:1px solid #e5e7eb;justify-content:flex-end;align-items:center;padding-right:6px;display:flex;position:absolute;left:0;right:0;overflow:hidden}.aqsp-body{position:absolute;inset:0}.aqsp-row{cursor:default}.aqsp-cell{white-space:nowrap;background:#fff;border-bottom:1px solid #e5e7eb;border-right:1px solid #e5e7eb;align-items:center;padding:0 4px;display:flex}.aqsp-cell[data-aqsp-valign=top]{align-items:flex-start}.aqsp-cell[data-aqsp-valign=bottom]{align-items:flex-end}.aqsp-cell[data-aqsp-wrap]{white-space:pre-wrap;overflow-wrap:anywhere}.aqsp-cell[data-aqsp-note]:after,.aqsp-cell[data-aqsp-comment]:after{content:"";pointer-events:none;border:5px solid #4b5563;border-color:#4b5563 #4b5563 #0000 #0000;width:0;height:0;position:absolute;top:0;right:0}.aqsp-cell[data-aqsp-comment]:after{border-top-color:#f97316;border-right-color:#f97316}.aqsp-cell[data-aqsp-invalid]:before{content:"";pointer-events:none;border:5px solid #0000;border-color:#0000 #0000 #dc2626 #dc2626;width:0;height:0;position:absolute;bottom:0;left:0}.aqsp-cell[data-aqsp-link]{color:#1d4ed8;text-decoration:underline}.aqsp-body-stack{grid-area:2/2;position:relative}.aqsp-overlay{pointer-events:none;position:absolute;inset:0;overflow:hidden}.aqsp-overlay-quad{pointer-events:none;position:absolute;overflow:hidden}.aqsp-selection-fill{mix-blend-mode:multiply;background:#3b82f62e;border:1px solid #2563ebd9;position:absolute;top:0;left:0}.aqsp-active-frame{box-sizing:border-box;border:2px solid #2563eb;position:absolute;top:0;left:0}.aqsp-drag-rect{background:#3b82f61a;border:1px solid #2563eb;position:absolute;top:0;left:0}.aqsp-drag-rect[data-aqsp-drag-mode=subtract]{background:#dc262614;border:1px dashed #dc2626}.aqsp-drag-rect[data-aqsp-drag-mode=point]{background:#16a34a14;border:1px dashed #16a34a}.aqsp-band{opacity:var(--aqsp-band-opacity);pointer-events:none;position:absolute;top:0;left:0}.aqsp-band[data-aqsp-band=row]{background:var(--aqsp-band-row)}.aqsp-band[data-aqsp-band=column]{background:var(--aqsp-band-column)}.aqsp-band[data-aqsp-band=intersection]{background:var(--aqsp-band-intersection)}.aqsp-clip-marquee{position:absolute;top:0;left:0}.aqsp-clip-marquee:after{content:"";top:var(--aqsp-clip-outset-top,-3px);right:var(--aqsp-clip-outset-right,-3px);bottom:var(--aqsp-clip-outset-bottom,-3px);left:var(--aqsp-clip-outset-left,-3px);background-image:linear-gradient(90deg,#2563eb 50%,#fff 50%),linear-gradient(90deg,#2563eb 50%,#fff 50%),linear-gradient(#fff 50%,#2563eb 50%),linear-gradient(#fff 50%,#2563eb 50%);background-position:0 0,8px 100%,0 8px,100% 0;background-repeat:repeat-x,repeat-x,repeat-y,repeat-y;background-size:8px 2px,8px 2px,2px 8px,2px 8px;animation:.4s linear infinite aqsp-marching-ants;position:absolute}@keyframes aqsp-marching-ants{to{background-position:8px 0,0 100%,0 0,100% 8px}}@media (prefers-reduced-motion:reduce){.aqsp-clip-marquee:after{animation:none}}.aqsp-col-resize-strip{cursor:col-resize;height:100%;position:absolute;top:0}.aqsp-row-resize-strip{cursor:row-resize;position:absolute;left:0;right:0}.aqsp-col-header .aqsp-unhide-marker{cursor:col-resize;opacity:.7;background:repeating-linear-gradient(90deg,#2563eb 0 1px,#0000 1px 3px);height:100%;position:absolute;top:0}.aqsp-row-header .aqsp-unhide-marker{cursor:row-resize;opacity:.7;background:repeating-linear-gradient(#2563eb 0 1px,#0000 1px 3px);position:absolute;left:0;right:0}.aqsp-resize-guide{pointer-events:none;background:#2563eb;position:absolute;top:0;left:0}.aqsp-autofill-handle{box-sizing:content-box;pointer-events:auto;cursor:crosshair;background:#2563eb;border:1px solid #fff;width:6px;height:6px;position:absolute;top:0;left:0}.aqsp-autofill-preview{border:1px dashed #16a34a;position:absolute;top:0;left:0}@media (forced-colors:active){.aqsp-band{outline-offset:-2px;background:0 0;outline:2px solid highlight}.aqsp-cell[data-aqsp-note]:after,.aqsp-cell[data-aqsp-comment]:after,.aqsp-cell[data-aqsp-invalid]:before{border:1px solid canvastext;width:6px;height:6px}.aqsp-drag-rect[data-aqsp-drag-mode=point]{background:0 0;border-color:highlight}.aqsp-selection-fill{outline-offset:-2px;background:0 0;border:none;outline:2px solid highlight}.aqsp-clip-marquee:after{background-image:none;border:2px dashed highlight;animation:none}}.aqsp-col-header-cell[data-aqsp-in-selection],.aqsp-row-header-cell[data-aqsp-in-selection]{color:#1d4ed8;background:#dbeafe;font-weight:600}.aqsp-live-region{clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.aqsp-sink{opacity:0;resize:none;pointer-events:auto;cursor:default;border:0;width:1px;height:1px;padding:0;position:absolute}.aqsp-sink[data-aqsp-editing]{opacity:1;z-index:1;box-sizing:border-box;color:#1f2937;caret-color:#1f2937;white-space:pre;outline-offset:-2px;cursor:text;background:#fff;outline:2px solid #2563eb;padding:0 4px;font-family:inherit;font-size:13px;line-height:1;overflow:hidden}.aqsp-sink[data-aqsp-pointing]{caret-color:#16a34a}.aqsp-popover-layer{pointer-events:none;position:absolute;inset:0;overflow:visible}.aqsp-popover-anchor{pointer-events:none;position:absolute;top:0;left:0}.aqsp-popover-anchor[inert]{visibility:hidden}.aqsp-popover{color:#1f2937;text-align:start;white-space:normal;cursor:default;pointer-events:auto;background:#fff;border:1px solid #d1d5db;border-radius:8px;flex-direction:column;width:320px;max-width:320px;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Noto Sans JP,sans-serif;font-size:13px;line-height:1.4;display:flex;position:absolute;overflow:hidden;box-shadow:0 1px 2px #00000014,0 8px 24px #00000024}.aqsp-popover:focus-visible{outline-offset:-2px;outline:2px solid #2563eb}.aqsp-popover[data-aqsp-popover-frame=note-tip]{background:#fffbeb;border-color:#f59e0b;box-shadow:0 2px 8px #0000001f}.aqsp-popover-note,.aqsp-popover-comment,.aqsp-popover-link,.aqsp-popover-message{flex-direction:column;min-height:0;padding:10px 12px;display:flex}.aqsp-popover-list{flex-direction:column;min-height:0;padding:6px;display:flex}.aqsp-popover-note-tip{overscroll-behavior:contain;white-space:pre-wrap;overflow-wrap:anywhere;min-height:0;padding:8px 10px;overflow-y:auto}[data-aqsp-popover][data-aqsp-popover-rejected]{border-inline-start:3px solid #dc2626;padding-inline-start:9px}.aqsp-popover-title{color:#374151;align-items:baseline;gap:6px;margin:0 0 6px;font-size:12px;font-weight:600;display:flex}.aqsp-popover-count{color:#6b7280;font-weight:400}.aqsp-popover-textarea,.aqsp-popover-input{width:100%;color:inherit;font:inherit;background:#fff;border:1px solid #d1d5db;border-radius:6px;margin:0;padding:5px 8px;line-height:1.4;display:block}.aqsp-popover-textarea{resize:none;white-space:pre-wrap;flex:auto;height:104px;min-height:56px;overflow:auto}.aqsp-popover-comment .aqsp-popover-textarea{flex:none;height:64px}.aqsp-popover-input{flex:none;height:28px}.aqsp-popover-textarea:focus-visible,.aqsp-popover-input:focus-visible{outline-offset:-1px;border-color:#2563eb;outline:2px solid #2563eb}.aqsp-popover-textarea[aria-invalid=true],.aqsp-popover-input[aria-invalid=true]{border-color:#dc2626}.aqsp-popover-textarea[aria-invalid=true]:focus-visible,.aqsp-popover-input[aria-invalid=true]:focus-visible{border-color:#dc2626;outline-color:#dc2626}.aqsp-popover-hint{color:#6b7280;margin:4px 0 0;font-size:12px}[data-aqsp-popover-rejected] .aqsp-popover-hint{color:#b91c1c}.aqsp-popover-field{flex-direction:column;flex:none;gap:3px;margin:0 0 8px;display:flex}.aqsp-popover-field-label{color:#6b7280;font-size:12px}.aqsp-popover-actions{flex:none;align-items:center;gap:6px;margin-top:8px;display:flex}.aqsp-popover-button{color:#1f2937;min-width:56px;height:26px;font:inherit;cursor:pointer;background:#f9fafb;border:1px solid #d1d5db;border-radius:6px;justify-content:center;align-items:center;margin:0;padding:0 10px;font-size:12px;font-weight:500;line-height:1;display:inline-flex}.aqsp-popover-button:not(:disabled):hover{background:#f3f4f6;border-color:#9ca3af}.aqsp-popover-button:focus-visible{outline-offset:1px;outline:2px solid #2563eb}.aqsp-popover-button:disabled{opacity:.5;cursor:not-allowed}.aqsp-popover-button[data-aqsp-popover-save],.aqsp-popover-button[data-aqsp-popover-post]{color:#fff;background:#2563eb;border-color:#2563eb}.aqsp-popover-button[data-aqsp-popover-save]:not(:disabled):hover,.aqsp-popover-button[data-aqsp-popover-post]:not(:disabled):hover{background:#1d4ed8;border-color:#1d4ed8}.aqsp-popover-button[data-aqsp-popover-close]{color:#6b7280;background:0 0;border-color:#0000;width:26px;min-width:0;margin-inline-start:auto;padding:0;font-size:16px;font-weight:400}.aqsp-popover-button[data-aqsp-popover-close]:not(:disabled):hover{color:#1f2937;background:#f3f4f6;border-color:#0000}.aqsp-popover-thread{overscroll-behavior:contain;flex:auto;min-height:48px;max-height:220px;margin:0 0 8px;padding:0;list-style:none;overflow-y:auto}.aqsp-popover-comment-item{border-top:1px solid #f3f4f6;padding:6px 0}.aqsp-popover-comment-item:first-child{border-top:0;padding-top:0}.aqsp-popover-comment-author{color:#111827;font-size:12px;font-weight:600}.aqsp-popover-comment-time{color:#6b7280;font-variant-numeric:tabular-nums;margin-inline-start:6px;font-size:11px}.aqsp-popover-comment-text{white-space:pre-wrap;overflow-wrap:anywhere;margin:2px 0 0}.aqsp-popover-list .aqsp-popover-input{margin-bottom:4px}.aqsp-popover-options{overscroll-behavior:contain;flex:auto;min-height:0;max-height:240px;overflow-y:auto}.aqsp-popover-option{white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:4px;padding:5px 8px;overflow:hidden}.aqsp-popover-option:hover{background:#f3f4f6}.aqsp-popover-option[aria-selected=true]{color:#1e3a8a;background:#dbeafe}.aqsp-popover-option:focus-visible{outline-offset:-2px;outline:2px solid #2563eb}.aqsp-popover-empty{color:#6b7280;padding:6px 8px;font-size:12px}.aqsp-popover-message{padding-inline-end:40px;position:relative}.aqsp-popover-message .aqsp-popover-title{margin-bottom:2px}.aqsp-popover-message-text{white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.aqsp-popover-message .aqsp-popover-button[data-aqsp-popover-close]{position:absolute;inset-block-start:6px;inset-inline-end:6px}.aqsp-popover-message[data-aqsp-popover-rejected]{background:#fef2f2}.aqsp-popover-message[data-aqsp-popover-rejected] .aqsp-popover-title{color:#b91c1c}@media (forced-colors:active){.aqsp-popover-option:hover,.aqsp-popover-option[aria-selected=true]{outline-offset:-2px;outline:2px solid highlight}.aqsp-popover-textarea[aria-invalid=true],.aqsp-popover-input[aria-invalid=true]{border-style:dashed}.aqsp-popover-button[data-aqsp-popover-close]{border-color:buttontext}}[data-aqsp-empty]{width:100%;height:100%;display:block}.aqsp-freeze-divider{background:var(--aqsp-divider-color,#9ca3af);pointer-events:none;position:absolute}.aqsp-freeze-divider-v{width:var(--aqsp-divider-width,2px)}.aqsp-freeze-divider-h{height:var(--aqsp-divider-width,2px)}@media (forced-colors:active){.aqsp-freeze-divider{background:canvastext}}}@layer utilities;
3
+ @layer theme,base;@layer components{@layer components{.aqvs-scroll-pane{display:flex;position:relative}.aqvs-scroll-pane[data-self-measured=true]{height:100%}.aqvs-scroll-pane-content{box-sizing:border-box;flex:1;height:100%;position:relative;overflow:hidden}.aqvs-tap-scroll-circle{touch-action:none;-webkit-user-select:none;user-select:none;justify-content:center;align-items:center;transition-property:transform;transition-duration:.1s;transition-timing-function:cubic-bezier(0,0,.2,1);display:flex;position:relative}.aqvs-tap-scroll-circle-visual-outer{border-radius:9999px;position:absolute;inset:0}.aqvs-tap-scroll-circle-gradient{background:linear-gradient(to bottom right,#1d4ed899,#60a5fa8c,#bfdbfe66);border:1px solid #fff6;border-radius:9999px;position:absolute;inset:0;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.aqvs-tap-scroll-circle-inner{border-radius:9999px;position:absolute;inset:18%}.aqvs-sample-visual-pupil{box-sizing:border-box;background-color:#ffffffd9;border:1px solid #ffffff80;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-sample-visual-highlight{background-color:#fffc;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-sample-visual-rod{background-color:#ffffff80;border-radius:9999px;position:absolute;top:50%;left:50%}.aqvs-scrollbar{z-index:50;cursor:default;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;touch-action:none;background-color:#fff;position:relative}.aqvs-scrollbar-horizontal{flex-direction:row;align-items:stretch;display:flex}.aqvs-scrollbar-vertical{flex-direction:column;align-items:stretch;display:flex}.aqvs-scrollbar[data-visible=false],.aqvs-scrollbar-thumb-wrapper[data-visible=false]{opacity:0;pointer-events:none}.aqvs-scrollbar-tap-circle-wrapper{pointer-events:auto;transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);position:absolute}.aqvs-scrollbar-arrow-button{color:#313131;letter-spacing:normal;background-color:#e0e0e0;border:0 solid;justify-content:center;align-items:center;padding:0;font-family:system-ui,sans-serif;font-size:.75rem;font-weight:400;line-height:1rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1);display:flex}.aqvs-scrollbar-arrow-button:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scrollbar-arrow-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.aqvs-scrollbar-arrow-button:disabled{cursor:not-allowed;opacity:.5}.aqvs-scrollbar-arrow-button:enabled:hover{background-color:#d4d4d4}.aqvs-scrollbar-track{background-color:#f5f5f5;flex:1;position:relative}.aqvs-scrollbar-overlay{pointer-events:none;position:absolute;inset:0}.aqvs-scrollbar-thumb-wrapper{touch-action:none;position:absolute}.aqvs-scrollbar-thumb{transform-origin:50%;background-color:#7f7f7f;transition:transform 80ms ease-out;position:absolute}.aqvs-scrollbar-thumb[data-thumb-state=disabled]{transition:none;transform:none}.aqvs-scrollbar-thumb[data-thumb-state=hover]{background-color:#5f5f5f}.aqvs-scrollbar-thumb[data-thumb-state=dragging]{background-color:#4f4f4f;transition:transform 60ms ease-out}.aqvs-scrollbar-thumb-horizontal{inset:1.5px 0}.aqvs-scrollbar-thumb-horizontal[data-thumb-state=hover]{top:-.5px;bottom:-.5px;transform:scaleY(1.06)}.aqvs-scrollbar-thumb-horizontal[data-thumb-state=dragging]{top:-.5px;bottom:-.5px;transform:scaleY(1.12)}.aqvs-scrollbar-thumb-vertical{inset:0 1.5px}.aqvs-scrollbar-thumb-vertical[data-thumb-state=hover]{left:-.5px;right:-.5px;transform:scaleX(1.06)}.aqvs-scrollbar-thumb-vertical[data-thumb-state=dragging]{left:-.5px;right:-.5px;transform:scaleX(1.12)}.aqvs-scroll-to-edge-button{pointer-events:auto;cursor:pointer;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;color:#fff;text-transform:uppercase;letter-spacing:.05em;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background-color:#1f2937cc;border:0 solid;border-radius:9999px;padding:.25rem 3rem;font-family:inherit;font-size:10px;font-weight:500;line-height:1.5;transition-property:transform,background-color;transition-duration:.15s;transform:none;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.aqvs-scroll-to-edge-button:focus{outline-offset:2px;outline:2px solid #0000}.aqvs-scroll-to-edge-button:focus-visible{outline-offset:1px;outline:2px solid #60a5fa}.aqvs-scroll-to-edge-button:hover{background-color:#374151}.aqvs-scroll-to-edge-button:active{transform:scale(.95)}.aqvs-scroll-to-edge-overlay{pointer-events:none;z-index:10;transition-property:opacity;transition-duration:.5s;position:absolute;inset:0}.aqvs-scroll-to-edge-overlay[data-visible=true]{opacity:1}.aqvs-scroll-to-edge-overlay[data-visible=false]{opacity:0}.aqvs-scroll-to-edge-overlay[data-visible=false] .aqvs-scroll-to-edge-button{pointer-events:none}.aqvs-scroll-to-edge-button-container{justify-content:center;display:flex;position:absolute;left:0;right:0}.aqvs-scroll-to-edge-button-container-top{top:.5rem}.aqvs-scroll-to-edge-button-container-bottom{bottom:.5rem}.aqvs-no-items-container{width:100%;position:absolute;top:0}.aqvs-no-items-text{text-align:center;color:#6b7280}.aqvs-item-container,.aqvs-bottom-inset,.aqvs-items-wrapper{width:100%;position:absolute}.aqvs-live-region{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.aqvs-grid{grid-template-rows:minmax(0,1fr) auto;width:100%;height:100%;display:grid;position:relative;overflow:hidden}.aqvs-grid-has-frozen-rows{grid-template-rows:auto minmax(0,1fr) auto}.aqvs-grid-has-trailing-rows{grid-template-rows:minmax(0,1fr) auto auto}.aqvs-grid-has-frozen-rows.aqvs-grid-has-trailing-rows{grid-template-rows:auto minmax(0,1fr) auto auto}.aqvs-grid-frozen-rows{min-width:0;position:relative;overflow:hidden}.aqvs-grid-frozen-band-row{position:absolute;left:0;overflow:hidden}.aqvs-grid-trailing-rows{min-width:0;position:relative;overflow:hidden}.aqvs-grid-trailing-rows-inner{position:absolute;bottom:0;left:0;right:0}.aqvs-grid-trailing-band-row{position:absolute;left:0;overflow:hidden}.aqvs-grid-main{min-width:0;min-height:0;position:relative}.aqvs-grid-row{width:100%;height:100%;transform:translateX(var(--aqvs-grid-hx-residual,0px));position:relative;overflow:visible}.aqvs-grid-cell{height:100%;position:absolute;top:0;overflow:hidden}.aqvs-grid-row.aqvs-grid-row-frozen-host{transform:none}.aqvs-grid-row-scroll{height:100%;top:0;left:var(--aqvs-grid-frozen-width,0px);right:var(--aqvs-grid-trailing-width,0px);position:absolute;overflow:hidden}.aqvs-grid-row-scroll-inner{width:100%;height:100%;transform:translateX(calc(var(--aqvs-grid-hx-residual,0px) - var(--aqvs-grid-frozen-width,0px)));position:absolute;top:0;left:0}.aqvs-grid-row-trailing{height:100%;top:0;left:max(var(--aqvs-grid-frozen-width,0px), calc(100% - var(--aqvs-grid-trailing-width,0px)));position:absolute;right:0;overflow:hidden}.aqvs-grid-row-trailing-inner{height:100%;width:var(--aqvs-grid-trailing-width,0px);position:absolute;top:0;right:0}.aqvs-grid-hbar-strip{min-width:0;padding-left:var(--aqvs-grid-frozen-width,0px);position:relative}.aqvs-grid-live-region{clip:rect(0 0 0 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.aqvs-grid-tap-circle-wrapper{z-index:50;pointer-events:auto;position:absolute}.aqvs-grid-tap-circle{transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}@media (forced-colors:active){.aqvs-tap-scroll-circle{outline-offset:-1px;border-radius:50%;outline:1px solid canvastext}}}.aqsp-root,.aqsp-root *,.aqsp-root :before,.aqsp-root :after{box-sizing:border-box}.aqsp-root{--aqsp-row-header-width:64px;--aqsp-col-header-height:24px;--aqsp-band-row:#0084ff;--aqsp-band-column:#42ff4f;--aqsp-band-intersection:#e1ff00;--aqsp-band-opacity:.2;color:#1f2937;background:#fff;width:100%;height:100%;font-size:13px;line-height:1;position:relative;overflow:hidden}.aqsp-zoom{grid-template-columns:var(--aqsp-row-header-width) minmax(0, 1fr);grid-template-rows:var(--aqsp-col-header-height) minmax(0, 1fr);transform-origin:0 0;width:100%;height:100%;display:grid}.aqsp-corner{background:#f3f4f6;border-bottom:1px solid #d1d5db;border-right:1px solid #d1d5db;grid-area:1/1}.aqsp-col-header{-webkit-user-select:none;user-select:none;background:#f3f4f6;border-bottom:1px solid #d1d5db;grid-area:1/2;position:relative;overflow:hidden}.aqsp-col-header-scroll{height:100%;position:absolute;top:0;right:0;overflow:hidden}.aqsp-row-header-scroll{width:100%;position:absolute;bottom:0;left:0;overflow:hidden}.aqsp-col-header-track{will-change:transform;position:absolute;inset:0}.aqsp-col-header-cell{white-space:nowrap;color:#4b5563;border-right:1px solid #e5e7eb;justify-content:center;align-items:center;height:100%;display:flex;position:absolute;top:0;overflow:hidden}.aqsp-row-header{-webkit-user-select:none;user-select:none;background:#f3f4f6;border-right:1px solid #d1d5db;grid-area:2/1;position:relative;overflow:hidden}.aqsp-row-header-track{will-change:transform;position:absolute;top:0;left:0;right:0}.aqsp-row-header-cell{white-space:nowrap;color:#4b5563;font-variant-numeric:tabular-nums;border-bottom:1px solid #e5e7eb;justify-content:flex-end;align-items:center;padding-right:6px;display:flex;position:absolute;left:0;right:0;overflow:hidden}.aqsp-body{position:absolute;inset:0}.aqsp-row{cursor:default}.aqsp-cell{white-space:nowrap;background:#fff;border-bottom:1px solid #e5e7eb;border-right:1px solid #e5e7eb;align-items:center;padding:0 4px;display:flex}.aqsp-cell[data-aqsp-valign=top]{align-items:flex-start}.aqsp-cell[data-aqsp-valign=bottom]{align-items:flex-end}.aqsp-cell[data-aqsp-wrap]{white-space:pre-wrap;overflow-wrap:anywhere}.aqsp-cell[data-aqsp-note]:after,.aqsp-cell[data-aqsp-comment]:after{content:"";pointer-events:none;border:5px solid #4b5563;border-color:#4b5563 #4b5563 #0000 #0000;width:0;height:0;position:absolute;top:0;right:0}.aqsp-cell[data-aqsp-comment]:after{border-top-color:#f97316;border-right-color:#f97316}.aqsp-cell[data-aqsp-invalid]:before{content:"";pointer-events:none;border:5px solid #0000;border-color:#0000 #0000 #dc2626 #dc2626;width:0;height:0;position:absolute;bottom:0;left:0}.aqsp-cell[data-aqsp-link]{color:#1d4ed8;text-decoration:underline}.aqsp-body-stack{grid-area:2/2;position:relative}.aqsp-overlay{pointer-events:none;position:absolute;inset:0;overflow:hidden}.aqsp-overlay-quad{pointer-events:none;position:absolute;overflow:hidden}.aqsp-selection-fill{mix-blend-mode:multiply;background:#3b82f62e;border:1px solid #2563ebd9;position:absolute;top:0;left:0}.aqsp-active-frame{box-sizing:border-box;border:2px solid #2563eb;position:absolute;top:0;left:0}.aqsp-drag-rect{background:#3b82f61a;border:1px solid #2563eb;position:absolute;top:0;left:0}.aqsp-drag-rect[data-aqsp-drag-mode=subtract]{background:#dc262614;border:1px dashed #dc2626}.aqsp-drag-rect[data-aqsp-drag-mode=point]{background:#16a34a14;border:1px dashed #16a34a}.aqsp-band{opacity:var(--aqsp-band-opacity);pointer-events:none;position:absolute;top:0;left:0}.aqsp-band[data-aqsp-band=row]{background:var(--aqsp-band-row)}.aqsp-band[data-aqsp-band=column]{background:var(--aqsp-band-column)}.aqsp-band[data-aqsp-band=intersection]{background:var(--aqsp-band-intersection)}.aqsp-clip-marquee{position:absolute;top:0;left:0}.aqsp-clip-marquee:after{content:"";top:var(--aqsp-clip-outset-top,-3px);right:var(--aqsp-clip-outset-right,-3px);bottom:var(--aqsp-clip-outset-bottom,-3px);left:var(--aqsp-clip-outset-left,-3px);background-image:linear-gradient(90deg,#2563eb 50%,#fff 50%),linear-gradient(90deg,#2563eb 50%,#fff 50%),linear-gradient(#fff 50%,#2563eb 50%),linear-gradient(#fff 50%,#2563eb 50%);background-position:0 0,8px 100%,0 8px,100% 0;background-repeat:repeat-x,repeat-x,repeat-y,repeat-y;background-size:8px 2px,8px 2px,2px 8px,2px 8px;animation:.4s linear infinite aqsp-marching-ants;position:absolute}@keyframes aqsp-marching-ants{to{background-position:8px 0,0 100%,0 0,100% 8px}}@media (prefers-reduced-motion:reduce){.aqsp-clip-marquee:after{animation:none}}.aqsp-col-resize-strip{cursor:col-resize;height:100%;position:absolute;top:0}.aqsp-row-resize-strip{cursor:row-resize;position:absolute;left:0;right:0}.aqsp-col-header .aqsp-unhide-marker{cursor:col-resize;opacity:.7;background:repeating-linear-gradient(90deg,#2563eb 0 1px,#0000 1px 3px);height:100%;position:absolute;top:0}.aqsp-row-header .aqsp-unhide-marker{cursor:row-resize;opacity:.7;background:repeating-linear-gradient(#2563eb 0 1px,#0000 1px 3px);position:absolute;left:0;right:0}.aqsp-resize-guide{pointer-events:none;background:#2563eb;position:absolute;top:0;left:0}.aqsp-autofill-handle{box-sizing:content-box;pointer-events:auto;cursor:crosshair;background:#2563eb;border:1px solid #fff;width:6px;height:6px;position:absolute;top:0;left:0}.aqsp-autofill-preview{border:1px dashed #16a34a;position:absolute;top:0;left:0}@media (forced-colors:active){.aqsp-band{outline-offset:-2px;background:0 0;outline:2px solid highlight}.aqsp-cell[data-aqsp-note]:after,.aqsp-cell[data-aqsp-comment]:after,.aqsp-cell[data-aqsp-invalid]:before{border:1px solid canvastext;width:6px;height:6px}.aqsp-drag-rect[data-aqsp-drag-mode=point]{background:0 0;border-color:highlight}.aqsp-selection-fill{outline-offset:-2px;background:0 0;border:none;outline:2px solid highlight}.aqsp-clip-marquee:after{background-image:none;border:2px dashed highlight;animation:none}}.aqsp-col-header-cell[data-aqsp-in-selection],.aqsp-row-header-cell[data-aqsp-in-selection]{color:#1d4ed8;background:#dbeafe;font-weight:600}.aqsp-live-region{clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.aqsp-sink{opacity:0;resize:none;pointer-events:auto;cursor:default;border:0;width:1px;height:1px;padding:0;position:absolute}.aqsp-sink[data-aqsp-editing]{opacity:1;z-index:1;box-sizing:border-box;color:#1f2937;caret-color:#1f2937;white-space:pre;outline-offset:-2px;cursor:text;background:#fff;outline:2px solid #2563eb;padding:0 4px;font-family:inherit;font-size:13px;line-height:1;overflow:hidden}.aqsp-sink[data-aqsp-pointing]{caret-color:#16a34a}.aqsp-popover-layer{pointer-events:none;position:absolute;inset:0;overflow:visible}.aqsp-popover-anchor{pointer-events:none;position:absolute;top:0;left:0}.aqsp-popover-anchor[inert]{visibility:hidden}.aqsp-popover{color:#1f2937;text-align:start;white-space:normal;cursor:default;pointer-events:auto;background:#fff;border:1px solid #d1d5db;border-radius:8px;flex-direction:column;width:320px;max-width:320px;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Noto Sans JP,sans-serif;font-size:13px;line-height:1.4;display:flex;position:absolute;overflow:hidden;box-shadow:0 1px 2px #00000014,0 8px 24px #00000024}.aqsp-popover:focus-visible{outline-offset:-2px;outline:2px solid #2563eb}.aqsp-popover[data-aqsp-popover-frame=note-tip]{background:#fffbeb;border-color:#f59e0b;box-shadow:0 2px 8px #0000001f}.aqsp-popover-note,.aqsp-popover-comment,.aqsp-popover-link,.aqsp-popover-message{flex-direction:column;min-height:0;padding:10px 12px;display:flex}.aqsp-popover-list{flex-direction:column;min-height:0;padding:6px;display:flex}.aqsp-popover-note-tip{overscroll-behavior:contain;white-space:pre-wrap;overflow-wrap:anywhere;min-height:0;padding:8px 10px;overflow-y:auto}[data-aqsp-popover][data-aqsp-popover-rejected]{border-inline-start:3px solid #dc2626;padding-inline-start:9px}.aqsp-popover-title{color:#374151;align-items:baseline;gap:6px;margin:0 0 6px;font-size:12px;font-weight:600;display:flex}.aqsp-popover-count{color:#6b7280;font-weight:400}.aqsp-popover-textarea,.aqsp-popover-input{width:100%;color:inherit;font:inherit;background:#fff;border:1px solid #d1d5db;border-radius:6px;margin:0;padding:5px 8px;line-height:1.4;display:block}.aqsp-popover-textarea{resize:none;white-space:pre-wrap;flex:auto;height:104px;min-height:56px;overflow:auto}.aqsp-popover-comment .aqsp-popover-textarea{flex:none;height:64px}.aqsp-popover-input{flex:none;height:28px}.aqsp-popover-textarea:focus-visible,.aqsp-popover-input:focus-visible{outline-offset:-1px;border-color:#2563eb;outline:2px solid #2563eb}.aqsp-popover-textarea[aria-invalid=true],.aqsp-popover-input[aria-invalid=true]{border-color:#dc2626}.aqsp-popover-textarea[aria-invalid=true]:focus-visible,.aqsp-popover-input[aria-invalid=true]:focus-visible{border-color:#dc2626;outline-color:#dc2626}.aqsp-popover-hint{color:#6b7280;margin:4px 0 0;font-size:12px}[data-aqsp-popover-rejected] .aqsp-popover-hint{color:#b91c1c}.aqsp-popover-field{flex-direction:column;flex:none;gap:3px;margin:0 0 8px;display:flex}.aqsp-popover-field-label{color:#6b7280;font-size:12px}.aqsp-popover-actions{flex:none;align-items:center;gap:6px;margin-top:8px;display:flex}.aqsp-popover-button{color:#1f2937;min-width:56px;height:26px;font:inherit;cursor:pointer;background:#f9fafb;border:1px solid #d1d5db;border-radius:6px;justify-content:center;align-items:center;margin:0;padding:0 10px;font-size:12px;font-weight:500;line-height:1;display:inline-flex}.aqsp-popover-button:not(:disabled):hover{background:#f3f4f6;border-color:#9ca3af}.aqsp-popover-button:focus-visible{outline-offset:1px;outline:2px solid #2563eb}.aqsp-popover-button:disabled{opacity:.5;cursor:not-allowed}.aqsp-popover-button[data-aqsp-popover-save],.aqsp-popover-button[data-aqsp-popover-post]{color:#fff;background:#2563eb;border-color:#2563eb}.aqsp-popover-button[data-aqsp-popover-save]:not(:disabled):hover,.aqsp-popover-button[data-aqsp-popover-post]:not(:disabled):hover{background:#1d4ed8;border-color:#1d4ed8}.aqsp-popover-button[data-aqsp-popover-close]{color:#6b7280;background:0 0;border-color:#0000;width:26px;min-width:0;margin-inline-start:auto;padding:0;font-size:16px;font-weight:400}.aqsp-popover-button[data-aqsp-popover-close]:not(:disabled):hover{color:#1f2937;background:#f3f4f6;border-color:#0000}.aqsp-popover-thread{overscroll-behavior:contain;flex:auto;min-height:48px;max-height:220px;margin:0 0 8px;padding:0;list-style:none;overflow-y:auto}.aqsp-popover-comment-item{border-top:1px solid #f3f4f6;padding:6px 0}.aqsp-popover-comment-item:first-child{border-top:0;padding-top:0}.aqsp-popover-comment-author{color:#111827;font-size:12px;font-weight:600}.aqsp-popover-comment-time{color:#6b7280;font-variant-numeric:tabular-nums;margin-inline-start:6px;font-size:11px}.aqsp-popover-comment-text{white-space:pre-wrap;overflow-wrap:anywhere;margin:2px 0 0}.aqsp-popover-list .aqsp-popover-input{margin-bottom:4px}.aqsp-popover-options{overscroll-behavior:contain;flex:auto;min-height:0;max-height:240px;overflow-y:auto}.aqsp-popover-option{white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:4px;padding:5px 8px;overflow:hidden}.aqsp-popover-option:hover{background:#f3f4f6}.aqsp-popover-option[aria-selected=true]{color:#1e3a8a;background:#dbeafe}.aqsp-popover-option:focus-visible{outline-offset:-2px;outline:2px solid #2563eb}.aqsp-popover-empty{color:#6b7280;padding:6px 8px;font-size:12px}.aqsp-popover-message{padding-inline-end:40px;position:relative}.aqsp-popover-message .aqsp-popover-title{margin-bottom:2px}.aqsp-popover-message-text{white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.aqsp-popover-message .aqsp-popover-button[data-aqsp-popover-close]{position:absolute;inset-block-start:6px;inset-inline-end:6px}.aqsp-popover-message[data-aqsp-popover-rejected]{background:#fef2f2}.aqsp-popover-message[data-aqsp-popover-rejected] .aqsp-popover-title{color:#b91c1c}@media (forced-colors:active){.aqsp-popover-option:hover,.aqsp-popover-option[aria-selected=true]{outline-offset:-2px;outline:2px solid highlight}.aqsp-popover-textarea[aria-invalid=true],.aqsp-popover-input[aria-invalid=true]{border-style:dashed}.aqsp-popover-button[data-aqsp-popover-close]{border-color:buttontext}}[data-aqsp-empty]{width:100%;height:100%;display:block}.aqsp-freeze-divider{background:var(--aqsp-divider-color,#9ca3af);pointer-events:none;position:absolute}.aqsp-freeze-divider-v{width:var(--aqsp-divider-width,2px)}.aqsp-freeze-divider-h{height:var(--aqsp-divider-width,2px)}@media (forced-colors:active){.aqsp-freeze-divider{background:canvastext}}}@layer utilities;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiquants/spreadsheet",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Excel-grade spreadsheet grid for React: multi-cell selection, TSV/HTML clipboard, in-place editing on top of @aiquants/virtualscroll",
5
5
  "sideEffects": [
6
6
  "**/*.css"
@@ -34,11 +34,12 @@
34
34
  "LICENSE.md"
35
35
  ],
36
36
  "peerDependencies": {
37
+ "@aiquants/virtualscroll": "^3.6.1",
37
38
  "react": "^19.2.7",
38
- "react-dom": "^19.2.7",
39
- "@aiquants/virtualscroll": "^3.5.0"
39
+ "react-dom": "^19.2.7"
40
40
  },
41
41
  "devDependencies": {
42
+ "@aiquants/virtualscroll": "3.6.1",
42
43
  "@playwright/test": "^1.59.1",
43
44
  "@tailwindcss/cli": "^4.1.18",
44
45
  "@testing-library/jest-dom": "^6.9.1",
@@ -55,8 +56,7 @@
55
56
  "typescript": "^5.9.3",
56
57
  "vite": "^7.2.7",
57
58
  "vite-plugin-dts": "^4.5.4",
58
- "vitest": "^3.2.4",
59
- "@aiquants/virtualscroll": "3.5.0"
59
+ "vitest": "^3.2.4"
60
60
  },
61
61
  "keywords": [
62
62
  "react",
@@ -39,9 +39,10 @@
39
39
  * openPopoverRef / hideNoteTipRef / pasteRef / pendingPasteOptionsRef / ポップオーバーアンカー) を所有する。
40
40
  */
41
41
 
42
- import type { VirtualGridHandle, VirtualGridRange } from "@aiquants/virtualscroll"
42
+ import type { ScrollBarTapCircleOptions, VirtualGridHandle, VirtualGridRange } from "@aiquants/virtualscroll"
43
43
  import type { ProfilerOnRenderCallback } from "react"
44
44
  import { forwardRef, type ReactNode, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react"
45
+ import type { ClipChannel } from "./clipboard/clipPayload.ts"
45
46
  import type { PasteOptions } from "./clipboard/pasteSpecial.ts"
46
47
  import { type ClipInfo, type PasteOutcome, type PasteSheet, useClipboard } from "./clipboard/useClipboard.ts"
47
48
  import { type BorderPreset, type CommandRunner, useCommands } from "./commands/useCommands.ts"
@@ -62,7 +63,7 @@ import { type CellCoord, type CellRange, clampCoord, makeRange, rangeSize, type
62
63
  import { type CellBorder, type CellFormat, type CellFormatPatch, formatCellValue } from "./model/cellFormat.ts"
63
64
  import { type EditPaint, IDLE_EDIT_PAINT, normalizeColor, resolveEditPaint } from "./model/cellPaint.ts"
64
65
  import { buildCfIndex, type CfIndex, type CfMemo, createCfMemo, resolvePaintedFormat } from "./model/conditionalFormat.ts"
65
- import { type CellEdit, type EditContext, type EditRejection, type EditSource, lazyCellText, resolveCellAlign, type SpreadsheetDataAdapter } from "./model/dataAdapter.ts"
66
+ import { type CellEdit, type EditContext, type EditRejection, type EditSource, isReadOnlyCell, lazyCellText, type ReadOnlyRejection, readOnlyDeclared, resolveCellAlign, type SpreadsheetDataAdapter } from "./model/dataAdapter.ts"
66
67
  import { type DataValidationRule, type ValidationVerdict, validateCellText } from "./model/dataValidation.ts"
67
68
  import type { ReferenceAxis } from "./model/formulaRefs.ts"
68
69
  import { colToViewportX, rowToViewportY } from "./model/frozenPartition.ts"
@@ -204,6 +205,55 @@ export type SpreadsheetProps = {
204
205
  * frozenTrailingColumns の行双子。エンジン契約・取消シームは同文)。典型は固定合計行。
205
206
  */
206
207
  frozenTrailingRows?: number
208
+ /**
209
+ * Tap-scroll circle tuning (v0.10 — the VirtualGrid `scrollBarOptions.tapScrollCircleOptions`
210
+ * engine-validated pass-through; LIVE prop): position and enable/disable of the touch
211
+ * tap-scroll circle. The engine's `resolveTapScrollCircleOptions` is the validation SSOT —
212
+ * undefined is the engine's declared absence signal (engine defaults), never a value
213
+ * fallback in this package.
214
+ * タップサークル調整 (v0.10 — VirtualGrid `scrollBarOptions.tapScrollCircleOptions` の
215
+ * エンジン検証素通し。ライブ prop): タッチのタップスクロールサークルの位置・有効 / 無効の
216
+ * 調整口。検証の SSOT はエンジンの `resolveTapScrollCircleOptions` — undefined はエンジン
217
+ * 宣言の不在信号 (エンジン既定) で、本パッケージでの値フォールバックなし。
218
+ */
219
+ tapScrollCircleOptions?: ScrollBarTapCircleOptions
220
+ /**
221
+ * Row-axis overscan pass-through (the VirtualGrid `overscanRows` knob; LIVE prop): rows
222
+ * rendered beyond each vertical viewport edge. This slack is the only thing absorbing the
223
+ * engine's one-frame rendering-window lag during fast scrolling (the blank-band remedy —
224
+ * the slack in px is overscan × row height per edge). Undefined ⇒ the engine default (3).
225
+ * The engine clamps invalid values silently, so THIS surface owns the fail-fast: a
226
+ * non-integer or negative value throws a RangeError at render. Cost multiplies with the
227
+ * column axis (bounded by the engine cell cap).
228
+ * 行軸 overscan の素通し (VirtualGrid `overscanRows`。ライブ prop): 縦 viewport 端の外側へ
229
+ * 描画する行数。高速スクロール時の描画窓 1 フレーム遅れを吸収する唯一の余裕 (白帯対策 —
230
+ * 余裕 px は片端あたり overscan × 行高)。undefined はエンジン既定 (3)。エンジンは不正値を
231
+ * 無言クランプするため fail-fast は本層所有 — 非整数・負値は render 時 RangeError。費用は
232
+ * 列軸との積で増幅 (エンジンのセル上限で有界)。
233
+ */
234
+ overscanRows?: number
235
+ /**
236
+ * Column-axis overscan pass-through (the VirtualGrid `overscanCols` knob; LIVE prop) — the
237
+ * column twin of `overscanRows`, same validation contract (non-negative integer or
238
+ * RangeError; undefined ⇒ engine default 3).
239
+ * 列軸 overscan の素通し (VirtualGrid `overscanCols`。ライブ prop) — `overscanRows` の
240
+ * 列双子。検証契約は同文 (非負整数、それ以外 RangeError。undefined ⇒ エンジン既定 3)。
241
+ */
242
+ overscanCols?: number
243
+ /**
244
+ * Background layer rendered behind the scroll-band rows, inside the engine content element
245
+ * (the VirtualGrid `background` pass-through; LIVE prop). VIEWPORT-PINNED: the engine
246
+ * translates only its row wrapper, so this layer does NOT follow the scroll offset —
247
+ * periodic row ruling placed here slides against the real rows while scrolling. Suited to
248
+ * a static texture that reads as "content still arriving" in the overscan-exhausted band,
249
+ * not to row-aligned gridlines.
250
+ * スクロール帯行の背後・エンジンコンテンツ要素内に描画する背景レイヤー (VirtualGrid
251
+ * `background` の素通し。ライブ prop)。viewport 固定: エンジンが translate するのは行
252
+ * ラッパーだけで、この層はスクロール量に追従しない — 周期的な行罫線を置くとスクロール中に
253
+ * 実際の行へ対して滑る。overscan が尽きた帯で「まだ到着中」と読める静的テクスチャ向きで、
254
+ * 行整合の罫線には不向き。
255
+ */
256
+ background?: ReactNode
207
257
  /**
208
258
  * Identity-stable contract (same as adapter): an inline arrow here defeats the CellView memo
209
259
  * and turns every host re-render into a full visible re-slice (~800 cells at 16k columns).
@@ -255,6 +305,39 @@ export type SpreadsheetProps = {
255
305
  * Escape は WCAG 2.1.2 のフォーカス脱出)。
256
306
  */
257
307
  onEditRejected?: (error: unknown, rejection: EditRejection) => void
308
+ /**
309
+ * Read-only refusal notice (v0.10): fired once per refused gesture — the adapter was never
310
+ * called, so this is neither an EditRejection nor an operation limit. Never fired when the
311
+ * adapter does not declare `isCellReadOnly`; omit for silence.
312
+ * 読み取り専用拒否通知 (v0.10)。拒否されたジェスチャ毎に 1 回発火 — adapter は呼ばれて
313
+ * いないため EditRejection でも操作上限でもない。`isCellReadOnly` 未宣言では発火せず、
314
+ * 省略時は通知なし。
315
+ */
316
+ onReadOnlyRejected?: (rejection: ReadOnlyRejection) => void
317
+ /**
318
+ * AT announcement text for a read-only refusal (v0.10), routed through the live region;
319
+ * omit for silence (no built-in strings). Inherits the announcement debounce + rAF collapse,
320
+ * which is what keeps autorepeat / IME refusal storms from spamming the live region.
321
+ * 読み取り専用拒否の AT 読み上げ文言 (v0.10) — live region 経由。省略で無音 (組み込み文言
322
+ * なし)。読み上げのデバウンス + rAF 合流を継承し、autorepeat / IME の拒否連打でも live
323
+ * region は溢れない。
324
+ */
325
+ formatReadOnlyAnnouncement?: (rejection: ReadOnlyRejection) => string
326
+ /**
327
+ * Paste outcome summary (v0.10 plan §3.1): the menu channel mirrors `paste(options)`'s
328
+ * resolve value 1:1 (read refusals and the write-time re-gate included); the event channel
329
+ * fires once per gesture that reaches the paste pipeline — the drag-freeze consumption and a
330
+ * null `clipboardData` stay deliberately silent, and an empty grid attaches no listener at
331
+ * all. Cause-specific callbacks (`onClipboardUnavailable` / `onOperationRejected` /
332
+ * `onReadOnlyRejected`) fire FIRST — `"rejected"` can be the trailing summary of one of
333
+ * them. Omit = never fired.
334
+ * 貼り付け結果の要約 (v0.10 計画 §3.1)。menu チャネルは `paste(options)` の resolve 値の
335
+ * 1:1 鏡 (読み拒否・書込時再ゲート含む)、event チャネルは貼り付けパイプラインに到達した
336
+ * ゲスチャ毎 1 回 — ドラッグ凍結消費と `clipboardData` null は意図的無言、空グリッドは
337
+ * リスナ自体が無い。原因特定系 callback が先に発火し、`"rejected"` はその後追いの要約に
338
+ * なり得る。省略時は発火なし。
339
+ */
340
+ onPasteResolved?: (outcome: PasteOutcome, channel: ClipChannel) => void
258
341
  /** Change notice for out-of-package writes (undo / collaboration); each increment repaints visible cells (§6.9-2). / パッケージ外経路のデータ変更通知。 */
259
342
  dataVersion?: number
260
343
  /** Operation cell-count ceiling, default 100,000 (ADR-12). / 操作セル数上限。 */
@@ -281,12 +364,19 @@ export type SpreadsheetProps = {
281
364
  * AT announcement text for the active cell; omit for silence (no built-in strings). `text`
282
365
  * is the RAW canonical getCellText; `displayText` is what a sighted user sees (the
283
366
  * getCellFormat-formatted string — equals `text` when the cell has no format), so AT can
284
- * hear "87.0%" for a cell whose raw value is "0.87".
367
+ * hear "87.0%" for a cell whose raw value is "0.87". `readOnly` (v0.10) is
368
+ * declaration-gated three-valued: the key is ABSENT when the adapter does not declare
369
+ * `isCellReadOnly` (payload byte-identical to v0.9), `false` = declared editable, `true` =
370
+ * declared read-only — the navigation-time read-only channel (DOM focus stays on the sink,
371
+ * so `aria-readonly` is inert during Arrow navigation).
285
372
  * AT 向け読み上げ文言 (省略で無音)。`text` は素の正準 getCellText、`displayText` は目視と同じ
286
373
  * 表示文字列 (getCellFormat 整形後 — 書式なしセルでは text と同値)。素値 "0.87" の表示
287
- * "87.0%" をそのまま読み上げられる。
374
+ * "87.0%" をそのまま読み上げられる。`readOnly` (v0.10) は宣言ゲートの 3 値: `isCellReadOnly`
375
+ * 未宣言はキー自体なし (payload は v0.9 とバイト同一)、`false` = 宣言済み編集可、`true` =
376
+ * 宣言済み読み取り専用 — 矢印航行時の読み取り専用チャネル (フォーカスはシンク常駐のため
377
+ * `aria-readonly` は航行中の AT に届かない)。
288
378
  */
289
- formatCellAnnouncement?: (ctx: { row: number; col: number; text: string; displayText: string; columnName: string }) => string
379
+ formatCellAnnouncement?: (ctx: { row: number; col: number; text: string; displayText: string; columnName: string; readOnly?: boolean }) => string
290
380
  /** AT announcement for the selection extent ("A1 to C3, 3 rows 3 cols"); omit for silence. / AT 向け選択範囲読み上げ。 */
291
381
  formatSelectionAnnouncement?: (ctx: { range: CellRange; anchor: CellCoord; active: CellCoord; rangeRowCount: number; rangeColCount: number; rangeCount: number }) => string
292
382
  /**
@@ -420,6 +510,21 @@ const ZOOM_MAX = 4
420
510
  * fail-fast — Math.min/max は NaN を素通しし、全 ÷z 境界の汚染と同値バウンド無効化 (NaN 同士は
421
511
  * 不一致) による onZoomChange(NaN) 連射を招くため。
422
512
  */
513
+ /**
514
+ * Validates one overscan prop and returns it unchanged (fail-fast — the engine clamps invalid
515
+ * overscan silently, so rejection is owned by this surface). Undefined is the declared absence
516
+ * signal (engine default) and is exempt. Throws RangeError on a non-integer or negative value.
517
+ * overscan prop 1 つの検証 (値はそのまま返す。fail-fast — エンジンは不正 overscan を無言
518
+ * クランプするため拒否は本層所有)。undefined は宣言不在信号 (エンジン既定) で対象外。非整数・
519
+ * 負値は RangeError。
520
+ */
521
+ const validateOverscan = (name: "overscanRows" | "overscanCols", value: number | undefined): number | undefined => {
522
+ if (value !== undefined && !(Number.isInteger(value) && value >= 0)) {
523
+ throw new RangeError(`[spreadsheet] ${name} must be a non-negative integer (got ${value})`)
524
+ }
525
+ return value
526
+ }
527
+
423
528
  const clampZoom = (value: number): number => {
424
529
  if (!Number.isFinite(value)) {
425
530
  throw new RangeError(`[spreadsheet] zoom must be a finite number (got ${value})`)
@@ -444,6 +549,17 @@ const stepZoom = (value: number, direction: 1 | -1): number => (direction > 0 ?
444
549
  type SpreadsheetTestProps = {
445
550
  /** Test-only CellView re-render counter; never passed in production. / テスト専用カウンタ。 */
446
551
  __onCellRender?: ProfilerOnRenderCallback
552
+ /**
553
+ * Test-only virtual-keyboard raise spy (v0.10 plan §6.4 / RC-G2t): called synchronously by
554
+ * the single `raiseVirtualKeyboard` funnel, so spy count ≡ raise executions — a React-batched
555
+ * raise-then-lower mutates no DOM (`inputmode` observer-blind) yet still counts. Never
556
+ * passed in production (zero calls when unset — the `__onCellRender` conduct).
557
+ * テスト専用の仮想キーボード招待スパイ (v0.10 計画 §6.4 / RC-G2t)。単一漏斗
558
+ * `raiseVirtualKeyboard` が同期で呼ぶため、計数 ≡ raise 実行 — React バッチの
559
+ * raise-then-lower は DOM 無変異 (`inputmode` の観測は盲目) でも計数には映る。production
560
+ * では渡さない (未指定 = 呼出しゼロ — `__onCellRender` と同じ品行)。
561
+ */
562
+ __onVirtualKeyboardRaise?: () => void
447
563
  }
448
564
 
449
565
  const DEFAULT_ROW_HEIGHT = 24
@@ -554,6 +670,10 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
554
670
  frozenRows = 0,
555
671
  frozenTrailingColumns = 0,
556
672
  frozenTrailingRows = 0,
673
+ tapScrollCircleOptions,
674
+ overscanRows,
675
+ overscanCols,
676
+ background,
557
677
  renderCell,
558
678
  onSelectionChange,
559
679
  onCellContextMenu,
@@ -562,6 +682,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
562
682
  onCopyMultiRange,
563
683
  onOperationRejected,
564
684
  onEditRejected,
685
+ onReadOnlyRejected,
686
+ formatReadOnlyAnnouncement,
565
687
  dataVersion,
566
688
  maxOperationCells = DEFAULT_MAX_OPERATION_CELLS,
567
689
  enableFillHandle = true,
@@ -584,6 +706,7 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
584
706
  onValidationRejected,
585
707
  onPasteSpecialRequest,
586
708
  onClipboardUnavailable,
709
+ onPasteResolved,
587
710
  now = Date.now,
588
711
  zoom,
589
712
  defaultZoom,
@@ -591,6 +714,7 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
591
714
  testId,
592
715
  className,
593
716
  __onCellRender,
717
+ __onVirtualKeyboardRaise,
594
718
  }: SpreadsheetProps & SpreadsheetTestProps,
595
719
  ref,
596
720
  ): ReactNode {
@@ -1098,6 +1222,28 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
1098
1222
  [],
1099
1223
  )
1100
1224
 
1225
+ /**
1226
+ * The single read-only refusal funnel (v0.10 plan §6.4 — the SSOT of RC-ANN / RC-B): every
1227
+ * gate (G1..G16) delivers its rejection here, which relays the callback AND the announcement
1228
+ * (`formatReadOnlyAnnouncement` → `announce()` — the debounce is inherited). Both props
1229
+ * omitted = silence = byte-identical; the read-only refusal never rides `onEditRejected` /
1230
+ * `onOperationRejected` (channel purity).
1231
+ * 読み取り専用拒否の単一漏斗 (v0.10 計画 §6.4 — RC-ANN / RC-B の SSOT)。全ゲート (G1..G16)
1232
+ * の拒否はここへ集まり、callback と読み上げ (`formatReadOnlyAnnouncement` → `announce()` —
1233
+ * デバウンス継承) の両方を運ぶ。両 prop 省略 = 無音 = バイト恒等。読み取り専用拒否を
1234
+ * `onEditRejected` / `onOperationRejected` へ流用しない (チャネル純度)。
1235
+ */
1236
+ const rejectReadOnly = useCallback(
1237
+ (rejection: ReadOnlyRejection) => {
1238
+ onReadOnlyRejected?.(rejection)
1239
+ // 読み上げは prop 宣言時だけ (省略 = 無音 — 組み込み文言なしの家訓)
1240
+ if (formatReadOnlyAnnouncement !== undefined) {
1241
+ announce(formatReadOnlyAnnouncement(rejection))
1242
+ }
1243
+ },
1244
+ [onReadOnlyRejected, formatReadOnlyAnnouncement, announce],
1245
+ )
1246
+
1101
1247
  /** The 3-tier announcement rule (§6.9-9, evaluated top-down). / 3 段機械判定 (上から順)。 */
1102
1248
  const evaluateAnnouncement = useCallback(
1103
1249
  (prev: SelectionState, next: SelectionState) => {
@@ -1115,7 +1261,18 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
1115
1261
  const raw = adapter.getCellText(a.row, a.col)
1116
1262
  // displayText は目視と同一の整形後文字列 (塗り書式 = base ⊕ CF — getCellFormat 未宣言は raw と同値)
1117
1263
  const painted = adapter.getCellFormat === undefined ? undefined : paintedFormatOf(a.row, a.col, () => raw)
1118
- announce(formatCellAnnouncement({ row: a.row, col: a.col, text: raw, displayText: formatCellValue(raw, painted === undefined ? undefined : painted.value), columnName: columnIndexToName(a.col) }))
1264
+ // S2 (v0.10 §6.4): readOnly キーは宣言ゲート — isCellReadOnly 未宣言はキー自体を出さず
1265
+ // payload は v0.9 とバイト同一、宣言時は正負両真理のライブ回答 (RC-S2)
1266
+ announce(
1267
+ formatCellAnnouncement({
1268
+ row: a.row,
1269
+ col: a.col,
1270
+ text: raw,
1271
+ displayText: formatCellValue(raw, painted === undefined ? undefined : painted.value),
1272
+ columnName: columnIndexToName(a.col),
1273
+ ...(readOnlyDeclared(adapter) ? { readOnly: isReadOnlyCell(adapter, a.row, a.col) } : {}),
1274
+ }),
1275
+ )
1119
1276
  }
1120
1277
  }
1121
1278
  // (1) 単一 1×1 レンジ → セル読み上げ (素のクリック / 矢印移動)
@@ -1229,14 +1386,37 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
1229
1386
  // OnTextInputTypeChanged で進行中の変換を強制終了する: 未編集 → IME 1 文字 → Enter が
1230
1387
  // 落ちた利用者報告の根因 — セッション基準の切替は compositionstart の中で属性を変えていた)
1231
1388
  const [virtualKeyboard, setVirtualKeyboard] = useState(false)
1389
+ /**
1390
+ * The ONE invitation-raising funnel (v0.10 plan §6.4 / RC-G2t): `setVirtualKeyboard(true)`
1391
+ * has exactly this production site, so the test seam's spy count ≡ raise executions by
1392
+ * construction — a React-batched raise-then-lower mutates no DOM yet still counts. The two
1393
+ * LOWERING sites (`onRootBlur`, the non-mouse root-capture pointerdown) call
1394
+ * `setVirtualKeyboard(false)` directly. The uniqueness invariant is machine-sealed by the
1395
+ * side-(iv) census of `readOnlyCensus.spec.ts` (a second raise site cannot land silently).
1396
+ * 招待 raise の単一漏斗 (v0.10 計画 §6.4 / RC-G2t)。`setVirtualKeyboard(true)` の production
1397
+ * 出現はここだけ — テストシームのスパイ計数 ≡ raise 実行が構成的に成立し、React バッチの
1398
+ * raise-then-lower も DOM 無変異のまま計数に映る。下ろし 2 サイト (`onRootBlur` / 非 mouse の
1399
+ * root capture pointerdown) は `setVirtualKeyboard(false)` を直呼びのまま。単一性の不変条件は
1400
+ * `readOnlyCensus.spec.ts` の側 (iv) census が機械封印 (第 2 raise サイトは無音で着地できない)。
1401
+ */
1402
+ const raiseVirtualKeyboard = useCallback(() => {
1403
+ __onVirtualKeyboardRaise?.()
1404
+ setVirtualKeyboard(true)
1405
+ }, [__onVirtualKeyboardRaise])
1232
1406
  const onRequestEdit = useCallback(
1233
- (_cell: CellCoord, caretIndex?: number) => {
1234
- // ポインタで開くエディタだけが仮想キーボードを招待する (キーボード無し端末で開ける唯一の経路)。
1235
- // 変換は進行中でない (dblclick 押下面が先に requestCommit 済み) — 属性変更は安全
1236
- setVirtualKeyboard(true)
1407
+ (cell: CellCoord, caretIndex?: number) => {
1408
+ // G2 (v0.10 §6.4 / 設計 ADR-29 v3.1 — 招待前置が規範): raise は startEdit より先に走る
1409
+ // ため、読み取り専用セルでは招待自体をスキップする。G1 だけでは OSK が拒否より先に
1410
+ // 上がってしまう (キーボード無し端末のフラッシュ)。skip でも startEdit は呼ぶ —
1411
+ // 拒否発火サイトは G1 の 1 箇所 (SSOT)。能力未宣言は従来どおり無条件 raise (バイト恒等)
1412
+ if (!isReadOnlyCell(adapter, cell.row, cell.col)) {
1413
+ // ポインタで開くエディタだけが仮想キーボードを招待する (キーボード無し端末で開ける唯一の経路)。
1414
+ // 変換は進行中でない (dblclick 押下面が先に requestCommit 済み) — 属性変更は安全
1415
+ raiseVirtualKeyboard()
1416
+ }
1237
1417
  startEdit("edit", caretIndex)
1238
1418
  },
1239
- [startEdit],
1419
+ [adapter, raiseVirtualKeyboard, startEdit],
1240
1420
  )
1241
1421
  const getClipRef = useRef<(() => ClipInfo | null) | null>(null)
1242
1422
  const getClip = useCallback(() => getClipRef.current?.() ?? null, [])
@@ -1595,6 +1775,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
1595
1775
  commitEdits,
1596
1776
  maxOperationCells,
1597
1777
  onOperationRejected,
1778
+ // v0.10: G7 の拒否漏斗 (§6.4)
1779
+ rejectReadOnly,
1598
1780
  writeFillPreview: onFillFrame,
1599
1781
  repaintOverlay,
1600
1782
  xAutoScroller: fillXAutoScroller,
@@ -1685,6 +1867,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
1685
1867
  rearmSink,
1686
1868
  discardClip,
1687
1869
  isDragging: isAnyDragging,
1870
+ // v0.10: G1 / G16 の拒否漏斗 (callback + 読み上げの SSOT — §6.4)
1871
+ rejectReadOnly,
1688
1872
  // Excel 同様、編集中もセルの水平揃えを保つ (数値セルは右揃えのまま編集)。getCellFormat 未宣言は
1689
1873
  // null = v0.1 恒等 (textarea の text-align を触らない)。宣言時は塗り書式 (base ⊕ CF) で判定し
1690
1874
  // text は遅延 1 回読み (v0.8 §4.2.13)
@@ -1746,6 +1930,9 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
1746
1930
  onClipChange,
1747
1931
  onClipboardUnavailable,
1748
1932
  onEditRejected,
1933
+ // v0.10: G4 / G5 / G6a / G6b の拒否漏斗 (§6.4) + 貼り付け結果の要約 (§3.1)
1934
+ rejectReadOnly,
1935
+ onPasteResolved,
1749
1936
  })
1750
1937
  getClipRef.current = clipboard.getClip
1751
1938
  discardClipRef.current = clipboard.discardClip
@@ -1775,6 +1962,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
1775
1962
  onHyperlinkOpen,
1776
1963
  anchors: popoverAnchorsRef,
1777
1964
  repaintOverlay,
1965
+ // v0.10: G14a / G14b / G14c の拒否漏斗 (§6.4)
1966
+ rejectReadOnly,
1778
1967
  })
1779
1968
  popoversRef.current = popovers
1780
1969
  openPopoverRef.current = popovers.open
@@ -1834,6 +2023,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
1834
2023
  announce,
1835
2024
  formatCommandAnnouncement,
1836
2025
  keyboardCellRect,
2026
+ // v0.10: G8–G13 / G15 の拒否漏斗 (§6.4)
2027
+ rejectReadOnly,
1837
2028
  })
1838
2029
  commandsRef.current = commands
1839
2030
  executeCommandRef.current = commands.executeCommand
@@ -1868,6 +2059,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
1868
2059
  commitEdits,
1869
2060
  maxOperationCells,
1870
2061
  onOperationRejected,
2062
+ // v0.10: G3 (Delete / Backspace) の拒否漏斗 (§6.4)
2063
+ rejectReadOnly,
1871
2064
  getClip,
1872
2065
  discardClip,
1873
2066
  isDragging: isDraggingAny,
@@ -2350,7 +2543,8 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
2350
2543
  }
2351
2544
 
2352
2545
  // live region は読み上げ prop のどれか 1 つでも宣言されたときだけ描く (コマンド通知も同じ領域へ)
2353
- const hasAnnouncement = formatCellAnnouncement !== undefined || formatSelectionAnnouncement !== undefined || formatCommandAnnouncement !== undefined
2546
+ // v0.10: formatReadOnlyAnnouncement も live region の存在条件 (拒否読み上げは announce() 経由 — §6.4)
2547
+ const hasAnnouncement = formatCellAnnouncement !== undefined || formatSelectionAnnouncement !== undefined || formatCommandAnnouncement !== undefined || formatReadOnlyAnnouncement !== undefined
2354
2548
 
2355
2549
  return (
2356
2550
  // biome-ignore lint/a11y/useSemanticElements: fieldset はフォーム意味論であり不適。role="group" は Escape 脱出の着地点に現在地名を与えるための選択 (§6.9-7 / §10-4)
@@ -2437,6 +2631,10 @@ export const Spreadsheet = forwardRef<SpreadsheetHandle, SpreadsheetProps & Spre
2437
2631
  frozenRows={frozenRows}
2438
2632
  frozenTrailingColumns={frozenTrailingColumns}
2439
2633
  frozenTrailingRows={frozenTrailingRows}
2634
+ tapScrollCircleOptions={tapScrollCircleOptions}
2635
+ overscanRows={validateOverscan("overscanRows", overscanRows)}
2636
+ overscanCols={validateOverscan("overscanCols", overscanCols)}
2637
+ background={background}
2440
2638
  renderCell={renderCell}
2441
2639
  onScroll={onScroll}
2442
2640
  onRangeChange={onRangeChange}