github-issue-tower-defence-management 1.142.1 → 1.142.2

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 (22) hide show
  1. package/.github/workflows/test.yml +1 -0
  2. package/CHANGELOG.md +7 -0
  3. package/bin/adapter/entry-points/console/ui-dist/assets/index-2c5E3tdu.js +81 -0
  4. package/bin/adapter/entry-points/console/ui-dist/assets/index-BaGbcbYI.css +2 -0
  5. package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
  6. package/bin/adapter/repositories/GoogleSpreadsheetRepository.js +10 -2
  7. package/bin/adapter/repositories/GoogleSpreadsheetRepository.js.map +1 -1
  8. package/package.json +39 -39
  9. package/src/adapter/entry-points/console/ui/vite.config.ts +1 -1
  10. package/src/adapter/entry-points/console/ui-dist/assets/index-2c5E3tdu.js +81 -0
  11. package/src/adapter/entry-points/console/ui-dist/assets/index-BaGbcbYI.css +2 -0
  12. package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
  13. package/src/adapter/repositories/GoogleSpreadsheetRepository.integration.test.ts +189 -55
  14. package/src/adapter/repositories/GoogleSpreadsheetRepository.test.ts +63 -5
  15. package/src/adapter/repositories/GoogleSpreadsheetRepository.ts +17 -2
  16. package/src/adapter/repositories/LocalStorageRepository.integration.test.ts +1 -1
  17. package/types/adapter/repositories/GoogleSpreadsheetRepository.d.ts +1 -0
  18. package/types/adapter/repositories/GoogleSpreadsheetRepository.d.ts.map +1 -1
  19. package/bin/adapter/entry-points/console/ui-dist/assets/index-BTtIpfv_.css +0 -1
  20. package/bin/adapter/entry-points/console/ui-dist/assets/index-CMK3yyfo.js +0 -103
  21. package/src/adapter/entry-points/console/ui-dist/assets/index-BTtIpfv_.css +0 -1
  22. package/src/adapter/entry-points/console/ui-dist/assets/index-CMK3yyfo.js +0 -103
@@ -0,0 +1,2 @@
1
+ /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-font-weight:initial;--tw-outline-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-medium:500;--font-weight-semibold:600;--radius-md:.375rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-input:#e5e5e5;--color-ring:#0a0a0a;--color-background:#fff;--color-foreground:#0a0a0a;--color-primary:#171717;--color-primary-foreground:#fafafa;--color-secondary:#f5f5f5;--color-secondary-foreground:#171717;--color-accent:#f5f5f5;--color-accent-foreground:#171717}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.relative{position:relative}.static{position:static}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.block{display:block}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.items-center{align-items:center}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-input{border-color:var(--color-input)}.border-transparent{border-color:#0000}.bg-background{background-color:var(--color-background)}.bg-primary{background-color:var(--color-primary)}.bg-secondary{background-color:var(--color-secondary)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-2{padding-block:calc(var(--spacing) * 2)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.whitespace-nowrap{white-space:nowrap}.text-foreground{color:var(--color-foreground)}.text-primary-foreground{color:var(--color-primary-foreground)}.text-secondary-foreground{color:var(--color-secondary-foreground)}.lowercase{text-transform:lowercase}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.hover\:bg-accent:hover{background-color:var(--color-accent)}.hover\:bg-primary\/90:hover{background-color:#171717e6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--color-primary) 90%, transparent)}}.hover\:bg-secondary\/80:hover{background-color:#f5f5f5cc}@supports (color:color-mix(in lab, red, red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, var(--color-secondary) 80%, transparent)}}.hover\:text-accent-foreground:hover{color:var(--color-accent-foreground)}}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:var(--color-ring)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}}html{font-size:clamp(15px,2.1vw,26px)}body{color:#e6edf3;background-color:#0d1117;margin:0;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif}.console-app{flex-direction:column;width:100%;min-height:100dvh;display:flex}.console-tabbar{-webkit-overflow-scrolling:touch;background:#161b22;border-bottom:2px solid #30363d;flex-wrap:nowrap;align-items:stretch;gap:0;min-height:42px;padding:0 8px;display:flex;overflow-x:auto}.console-tab{color:#8b949e;white-space:nowrap;cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;align-items:center;gap:6px;margin-bottom:-2px;padding:10px 14px;font-size:.8125rem;font-weight:500;line-height:1.2;text-decoration:none;display:inline-flex}.console-tab:hover{color:#e6edf3}.console-tab[data-active=true]{color:#e6edf3;border-bottom-color:#2f81f7;font-weight:700}.console-tab-badge{text-align:center;color:#e6edf3;background:#484f58;border-radius:20px;min-width:20px;padding:1px 7px;font-size:.6875rem;font-weight:700;line-height:1.5}.console-tab-badge[data-zero=true]{color:#8b949e;background:#30363d}.console-tab-pjname{color:#8b949e;align-self:center;margin-left:auto;padding:0 8px;font-size:.7188rem}.console-tab-geninfo{color:#8b949e;align-self:center;padding:0 4px;font-size:.6875rem}.console-list{margin:0;padding:12px 18px 18px;list-style:none}.console-list-group{list-style:none}.console-item-row .console-item-icon{flex:none;margin-top:3px}.console-group-header{background:#0b0f14;border-bottom:1px solid #21262d;justify-content:space-between;align-items:center;padding:6px 12px;display:flex}.console-storytag{align-items:center;gap:8px;font-size:.8125rem;font-weight:700;display:inline-flex}.console-story-dot{border-radius:999px;width:10px;height:10px;display:inline-block}.console-group-count{color:#8b949e;font-size:.75rem}.console-item-row{color:#e6edf3;text-align:left;cursor:pointer;background:#161b22;border:1px solid #30363d;border-radius:8px;align-items:flex-start;gap:14px;width:100%;margin-bottom:10px;padding:12px 16px;display:flex}.console-item-row:hover{background:#1a2029;border-color:#484f58}.console-item-row[data-active=true]{background:#1a2029;border-color:#4493f8}.console-item-meta{flex:1;min-width:0}.console-item-title{font-size:.9063rem;font-weight:600;display:block}.console-item-sub{color:#8b949e;margin-top:3px;font-size:.7813rem;display:block}.console-item-pill{color:#8b949e;border:1px solid #30363d;border-radius:20px;margin-right:6px;padding:1px 8px;font-size:.6875rem;display:inline-block}.console-item-createdat{color:#8b949e;cursor:help}.console-item-fields{color:#adbac7;flex-wrap:wrap;gap:4px 10px;margin-top:4px;font-size:.7188rem;display:flex}.console-item-field{word-break:break-all;align-items:baseline;gap:4px;min-width:0;display:inline-flex}.console-item-field-label{color:#6e7681;text-transform:uppercase;letter-spacing:.03em;font-size:.625rem}.console-list-message{color:#8b949e;padding:16px;font-size:.875rem}.console-list-empty{text-align:center;color:#8b949e;padding:40px;font-size:.875rem}.console-list-error,.console-comment-error,.console-files-error,.console-commits-error,.console-detail-body-error{color:#f85149}.console-detail{flex-direction:column;flex:1 0 auto;gap:12px;padding:16px 16px 0;display:flex}.console-detail-title{align-items:center;gap:8px;margin:0;font-size:1.25rem;display:flex}.console-detail-title-text{flex:1}.console-detail-number{color:#8b949e;font-weight:400}.console-detail-closed-label{color:#a371f7;font-size:.8125rem}.console-detail-subbar{align-items:center;gap:12px;font-size:.8125rem;display:flex}.console-detail-link{color:#4493f8}.console-detail-repo{color:#8b949e;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.console-copy-url-button{color:#c9d1d9;border:1px solid #30363d;border-radius:6px;height:24px;padding:0 8px;font-size:.75rem}.console-copy-url-button:hover{color:#e6edf3;background-color:#21262d}.console-detail-pill,.console-label-chip,.console-detail-status-chip{border:1px solid #30363d;border-radius:999px;padding:2px 8px;font-size:.75rem;display:inline-block}.console-detail-pr-status-row{flex-wrap:wrap;align-items:center;gap:6px;margin-top:-4px;display:flex}.console-detail-pr-status{flex-wrap:wrap;align-items:center;gap:6px;display:inline-flex}.console-detail-ci-missing{opacity:.85;font-weight:400}.console-detail-mergeable-chip{font-weight:600}.console-detail-mergeable-chip-unknown{opacity:.7;font-weight:400}.console-detail-labels{flex-wrap:wrap;gap:6px;display:flex}.console-detail-createdat{color:#8b949e;font-size:.75rem}.console-panel{border:1px solid #30363d;border-radius:8px;overflow:hidden}.console-panel-header{background:#161b22;justify-content:space-between;align-items:center;display:flex}.console-panel-toggle{color:#e6edf3;text-align:left;cursor:pointer;background:0 0;border:none;flex:1;align-items:center;gap:8px;padding:6px 12px;font-size:.875rem;font-weight:600;display:flex}.console-panel-toggle:hover{background:#1a2029}.console-panel-action{padding:6px 12px}.console-panel-body{padding:12px}.console-markdown{word-break:break-word;font-size:.875rem;line-height:1.5}.console-markdown ul{margin:.5em 0;padding-left:1.5em;list-style:outside}.console-markdown ol{margin:.5em 0;padding-left:1.5em;list-style:decimal}.console-markdown li{margin:.25em 0}.console-markdown table{border-collapse:collapse;margin:.5em 0}.console-markdown th,.console-markdown td{border:1px solid #30363d;padding:4px 8px}.console-markdown th{background:#161b22}.console-markdown h1,.console-markdown h2,.console-markdown h3,.console-markdown h4,.console-markdown h5,.console-markdown h6{margin:1em 0 .5em;font-weight:700;line-height:1.25}.console-markdown h1{font-size:1.6em}.console-markdown h2{font-size:1.4em}.console-markdown h3{font-size:1.2em}.console-markdown h4{font-size:1.05em}.console-markdown h5{font-size:.95em}.console-markdown h6{color:#8b949e;font-size:.9em}.console-markdown pre{background:#161b22;border:1px solid #30363d;border-radius:6px;margin:.5em 0;padding:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8125rem;line-height:1.45;overflow-x:auto}.console-markdown code{background:#6e768166;border-radius:4px;padding:.15em .4em;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em}.console-markdown pre code{font-size:inherit;background:0 0;border-radius:0;padding:0}.console-markdown blockquote{color:#8b949e;border-left:3px solid #30363d;margin:.5em 0;padding:0 1em}.console-markdown a{color:#4493f8;text-decoration:none}.console-markdown a:hover{text-decoration:underline}.console-markdown-reference-host{display:inline}.console-markdown-reference{vertical-align:baseline;align-items:center;gap:.25em;display:inline-flex}.console-markdown-reference .console-item-icon{flex-shrink:0}.console-markdown-reference-title{text-decoration:none}.console-markdown-reference:hover .console-markdown-reference-title{text-decoration:underline}.console-markdown hr{border:none;border-top:1px solid #30363d;margin:1em 0}.console-mermaid-error{color:#f85149;font-size:.8125rem}.console-comment{background:#161b22;border:1px solid #30363d;border-radius:8px;margin-bottom:10px;padding:12px 16px}.console-comment-header{color:#8b949e;gap:8px;font-size:.75rem;display:flex}.console-comment-show-all{color:#e6edf3;cursor:pointer;background:#21262d;border:1px solid #30363d;border-radius:6px;margin-bottom:10px;padding:6px 12px;font-size:.75rem;font-weight:600}.console-comment-show-all:hover{background-color:#262c34;border-color:#484f58}.console-comment-author{color:#e6edf3;font-weight:600}.console-files,.console-commits{margin:0;padding:0;list-style:none}.console-commit{align-items:center;gap:8px;padding:4px 0;font-size:.8125rem;display:flex}.console-file{flex-direction:column;font-size:.8125rem;display:flex}.console-file-tree,.console-file-tree-children{margin:0;padding:0;list-style:none}.console-file-tree-dir{flex-direction:column;display:flex}.console-file-tree-dir-name{color:#8b949e;align-items:center;gap:6px;padding:4px 0;font-weight:600;display:flex}.console-file-tree-dir-icon{flex:none}.console-file-badge{text-align:center;border:1px solid;border-radius:4px;width:18px;font-size:.6875rem}.console-file-path,.console-commit-message{flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.console-file-add,.console-pr-add{color:#3fb950}.console-file-del,.console-pr-del{color:#f85149}.console-commit-sha{color:#8b949e;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.console-actionbar{z-index:100;padding:10px 16px;padding-bottom:calc(10px + env(safe-area-inset-bottom));background:#161b22;border-top:2px solid #30363d;margin-top:auto;position:sticky;bottom:0}.console-operation-bar{flex-direction:column;gap:8px;width:100%;display:flex}.console-op-group{flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:8px;display:flex}.console-op-group-review{gap:10px}.console-op-group-stories{max-height:33vh;overflow-y:auto}.console-op-button{color:#e6edf3;white-space:nowrap;cursor:pointer;background:#21262d;border:1px solid #30363d;border-radius:6px;padding:9px 16px;font-size:.8125rem;font-weight:600}.console-op-button:hover{border-color:#484f58}.console-op-button-approve{color:#fff;background:#238636;border-color:#2ea043}.console-op-button-reject{color:#ffd166;background:#7d5000;border-color:#a06800}.console-op-button-wrong{color:#f85149;background:#3a1518;border-color:#f85149}.console-op-button-unneeded{color:#aab0b8;background:#2a2d31;border-color:#6e7681}.console-op-button-snooze{color:#79c0ff;background:#1c2b4a;border-color:#4493f8}.console-pr-section{border:1px solid #30363d;border-radius:8px;flex-direction:column;gap:10px;padding:12px;display:flex}.console-pr-statbar{color:#8b949e;gap:12px;font-size:.75rem;display:flex}.console-detail-screen{flex-direction:column;flex:1 0 auto;display:flex}.console-panel-open-link{color:#4493f8;font-size:.8125rem;font-weight:400}.console-composer{margin-top:4px}.console-composer-toggle{color:#8b949e;cursor:pointer;background:0 0;border:none;padding:2px 0;font-size:.7813rem}.console-composer-toggle:hover{color:#e6edf3}.console-composer-posted{flex-direction:column;gap:8px;margin-top:8px;display:flex}.console-composer-form{margin-top:8px}.console-composer-input{box-sizing:border-box;color:#e6edf3;width:100%;font:inherit;resize:vertical;background:#21262d;border:1px solid #30363d;border-radius:6px;padding:8px;font-size:.875rem}.console-composer-row{justify-content:flex-end;align-items:center;gap:8px;margin-top:6px;display:flex}.console-composer-submit{color:#fff;cursor:pointer;background:#238636;border:1px solid #2ea043;border-radius:6px;padding:7px 16px;font-size:.8125rem;font-weight:600}.console-composer-submit:disabled{opacity:.6;cursor:default}.console-composer-status{color:#8b949e;font-size:.75rem}.console-composer-error{color:#f85149}.console-undo-toast{z-index:9998;white-space:nowrap;border-radius:10px;align-items:center;gap:12px;max-width:92vw;padding:12px 18px;font-size:.8438rem;display:flex;position:fixed;top:12px;left:50%;transform:translate(-50%);box-shadow:0 4px 20px #0009}.console-undo-toast-green{color:#3fb950;background:#0d3320;border:1px solid #2ea043}.console-undo-toast-amber{color:#ffd166;background:#3a2800;border:1px solid #a06800}.console-undo-toast-red{color:#f85149;background:#3a1518;border:1px solid #da3633}.console-undo-toast-gray{color:#aab0b8;background:#2a2d31;border:1px solid #6e7681}.console-undo-toast-blue{color:#79c0ff;background:#0d1f40;border:1px solid #4493f8}.console-undo-toast-error{color:#f85149;background:#3a1518;border:1px solid #f85149}.console-undo-toast-message{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.console-undo-toast-undo{cursor:pointer;color:inherit;background:#ffffff1f;border:none;border-radius:6px;flex:none;padding:4px 12px;font-size:.8125rem;font-weight:700}.console-undo-toast-undo:hover{background:#ffffff38}.console-undo-toast-countdown{opacity:.7;flex:none;font-size:.6875rem}.console-undo-toast-bar{opacity:.5;background:currentColor;border-radius:0 0 10px 10px;height:3px;transition:width .1s linear;position:absolute;bottom:0;left:0}.console-file-row{width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;padding:4px 0;font-size:.8125rem;display:flex}.console-file-row:hover{background:#1a2029}.console-file-caret{color:#8b949e;flex:none;width:1em}.console-file-diff{border-collapse:collapse;width:100%;margin:4px 0 8px;font:.8125rem/1.6 ui-monospace,SFMono-Regular,Menlo,monospace}.console-file-diff td{white-space:pre-wrap;word-break:break-word;vertical-align:top;padding:0 10px}.console-diff-ln{text-align:right;color:#6e7681;-webkit-user-select:none;user-select:none;white-space:nowrap;border-right:1px solid #30363d;width:1%;padding:0 8px}.console-file-diff td.console-diff-ln{white-space:nowrap;word-break:normal;overflow-wrap:normal}.console-diff-add td{background:#2ea04326}.console-diff-add .console-diff-code{color:#aff5b4}.console-diff-del td{background:#f8514926}.console-diff-del .console-diff-code{color:#ffdcd7}.console-diff-hunk td{color:#79c0ff;background:#161b22}.console-diff-ctx .console-diff-code{color:#c9d1d9}.console-file-diff-empty{color:#8b949e;padding:8px 14px;font-size:.75rem}.console-diff-comment-cell{text-align:center;-webkit-user-select:none;user-select:none;width:1%;padding:0 2px}.console-diff-comment-button{color:#6e7681;cursor:pointer;opacity:.6;background:0 0;border:1px solid #30363d;border-radius:4px;width:18px;height:18px;padding:0;font-size:.875rem;line-height:16px}.console-diff-row:hover .console-diff-comment-button,.console-diff-comment-button:focus-visible,.console-diff-comment-button[aria-expanded=true]{opacity:1;color:#fff;background:#1f6feb;border-color:#2f81f7}.console-diff-composer-row td{background:#0d1117;padding:6px 10px}.console-diff-composer{background:#161b22;border:1px solid #30363d;border-radius:6px;flex-direction:column;gap:6px;padding:8px;display:flex}.console-diff-composer-anchor{color:#8b949e;font-size:.6875rem}.console-diff-composer-input{resize:vertical;color:#c9d1d9;width:100%;font:inherit;background:#0d1117;border:1px solid #30363d;border-radius:6px;padding:6px 8px}.console-diff-composer-controls{align-items:center;gap:8px;display:flex}.console-diff-composer-submit{color:#fff;cursor:pointer;background:#238636;border:1px solid #238636;border-radius:6px;padding:4px 12px}.console-diff-composer-submit:disabled{opacity:.6;cursor:default}.console-diff-composer-cancel{color:#c9d1d9;cursor:pointer;background:0 0;border:1px solid #30363d;border-radius:6px;padding:4px 12px}.console-diff-composer-status{color:#8b949e;font-size:.75rem}.console-diff-composer-error{color:#ff7b72}.console-diff-composer-posted{color:#3fb950;margin:0;font-size:.75rem}.console-pr-header{flex-wrap:wrap;align-items:center;gap:10px;padding-top:4px;display:flex}.console-pr-section-title{color:#e6edf3;font-size:.9375rem;font-weight:600}.console-pr-section-state{color:#8b949e;border:1px solid #6e7681;border-radius:999px;padding:1px 8px;font-size:.6875rem}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>TDPM Console</title>
7
- <script type="module" crossorigin src="/assets/index-CMK3yyfo.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-BTtIpfv_.css">
7
+ <script type="module" crossorigin src="/assets/index-2c5E3tdu.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-BaGbcbYI.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
@@ -1,4 +1,5 @@
1
1
  import dotenv from 'dotenv';
2
+ import { google } from 'googleapis';
2
3
  import { GoogleSpreadsheetRepository } from './GoogleSpreadsheetRepository';
3
4
  import { LocalStorageRepository } from './LocalStorageRepository';
4
5
 
@@ -6,6 +7,24 @@ dotenv.config();
6
7
 
7
8
  const GOOGLE_SERVICE_ACCOUNT_KEY = process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
8
9
 
10
+ const integrationTestIsRequired =
11
+ process.env.GOOGLE_SHEETS_INTEGRATION_REQUIRED === 'true';
12
+
13
+ const credentialIsMissingWhileRequired =
14
+ integrationTestIsRequired && !GOOGLE_SERVICE_ACCOUNT_KEY;
15
+
16
+ describe('GoogleSpreadsheetRepository integration test credential', () => {
17
+ test('is available whenever these integration tests are required to run', () => {
18
+ expect(
19
+ credentialIsMissingWhileRequired
20
+ ? 'GOOGLE_SERVICE_ACCOUNT_KEY is absent while GOOGLE_SHEETS_INTEGRATION_REQUIRED is true, so the Google Sheets integration tests would silently skip and prove nothing'
21
+ : 'the Google Sheets integration tests either hold their credential or are not required in this context',
22
+ ).toBe(
23
+ 'the Google Sheets integration tests either hold their credential or are not required in this context',
24
+ );
25
+ });
26
+ });
27
+
9
28
  const describeWhenCredentials = GOOGLE_SERVICE_ACCOUNT_KEY
10
29
  ? describe
11
30
  : describe.skip;
@@ -15,17 +34,101 @@ describeWhenCredentials('GoogleSpreadsheetRepository integration tests', () => {
15
34
  jest.retryTimes(3, { logErrorsBeforeRetry: true });
16
35
 
17
36
  const localStorageRepository = new LocalStorageRepository();
18
- const spreadsheetUrl =
19
- 'https://docs.google.com/spreadsheets/d/1N_3y0y46v5tHbra5YSm6PldflcsF1bkfeWDdQ3MRuXM/edit?gid=0#gid=0';
37
+ const spreadsheetId = '1N_3y0y46v5tHbra5YSm6PldflcsF1bkfeWDdQ3MRuXM';
38
+ const spreadsheetUrl = `https://docs.google.com/spreadsheets/d/${spreadsheetId}/edit?gid=0#gid=0`;
20
39
  const repository = new GoogleSpreadsheetRepository(
21
40
  localStorageRepository,
22
41
  GOOGLE_SERVICE_ACCOUNT_KEY,
23
42
  );
24
43
 
44
+ const scratchSheetNamePrefix = 'IntegrationScratch_';
45
+ const scratchSheetName = `${scratchSheetNamePrefix}${Date.now()}_${process.pid}`;
46
+ const abandonedScratchSheetAgeMilliseconds = 2 * 60 * 60 * 1000;
47
+ const rawSheetsClientKeyFile =
48
+ './tmp/integration-test-service-account-key.json';
49
+
50
+ const createRawSheetsClient = () => {
51
+ if (!GOOGLE_SERVICE_ACCOUNT_KEY) {
52
+ throw new Error(
53
+ 'GOOGLE_SERVICE_ACCOUNT_KEY is required to run the Google Sheets integration tests',
54
+ );
55
+ }
56
+ localStorageRepository.write(
57
+ rawSheetsClientKeyFile,
58
+ GOOGLE_SERVICE_ACCOUNT_KEY,
59
+ );
60
+ const auth = new google.auth.GoogleAuth({
61
+ keyFile: rawSheetsClientKeyFile,
62
+ scopes: ['https://www.googleapis.com/auth/spreadsheets'],
63
+ });
64
+ return google.sheets({ version: 'v4', auth });
65
+ };
66
+
67
+ const listSheetTitles = async (): Promise<string[]> => {
68
+ const response = await createRawSheetsClient().spreadsheets.get({
69
+ spreadsheetId,
70
+ });
71
+ return (response.data.sheets ?? [])
72
+ .map((sheet) => sheet.properties?.title)
73
+ .filter((title): title is string => typeof title === 'string');
74
+ };
75
+
76
+ const deleteSheetsByTitle = async (titles: string[]): Promise<void> => {
77
+ if (titles.length === 0) {
78
+ return;
79
+ }
80
+ const sheetsClient = createRawSheetsClient();
81
+ const response = await sheetsClient.spreadsheets.get({ spreadsheetId });
82
+ const sheetIds = (response.data.sheets ?? [])
83
+ .filter((sheet) => {
84
+ const title = sheet.properties?.title;
85
+ return typeof title === 'string' && titles.includes(title);
86
+ })
87
+ .map((sheet) => sheet.properties?.sheetId)
88
+ .filter((sheetId): sheetId is number => typeof sheetId === 'number');
89
+ if (sheetIds.length === 0) {
90
+ return;
91
+ }
92
+ await sheetsClient.spreadsheets.batchUpdate({
93
+ spreadsheetId,
94
+ requestBody: {
95
+ requests: sheetIds.map((sheetId) => ({ deleteSheet: { sheetId } })),
96
+ },
97
+ });
98
+ };
99
+
100
+ const findAbandonedScratchSheetTitles = async (): Promise<string[]> => {
101
+ const titles = await listSheetTitles();
102
+ return titles
103
+ .filter((title) => title.startsWith(scratchSheetNamePrefix))
104
+ .filter((title) => title !== scratchSheetName)
105
+ .filter((title) => {
106
+ const createdAtMilliseconds = Number(
107
+ title.slice(scratchSheetNamePrefix.length).split('_')[0],
108
+ );
109
+ return (
110
+ Number.isFinite(createdAtMilliseconds) &&
111
+ Date.now() - createdAtMilliseconds >
112
+ abandonedScratchSheetAgeMilliseconds
113
+ );
114
+ });
115
+ };
116
+
25
117
  beforeEach(async () => {
26
118
  await new Promise((resolve) => setTimeout(resolve, 2000));
27
119
  });
28
120
 
121
+ afterAll(async () => {
122
+ const abandonedTitles = await findAbandonedScratchSheetTitles();
123
+ await deleteSheetsByTitle([scratchSheetName, ...abandonedTitles]);
124
+ });
125
+
126
+ describe('getSpreadsheetId', () => {
127
+ test('extracts the spreadsheet id from the spreadsheet url', () => {
128
+ expect(repository.getSpreadsheetId(spreadsheetUrl)).toBe(spreadsheetId);
129
+ });
130
+ });
131
+
29
132
  describe('getSheet', () => {
30
133
  const testCases: [string, string[][] | null][] = [
31
134
  ['SheetUndefined', null],
@@ -56,65 +159,96 @@ describeWhenCredentials('GoogleSpreadsheetRepository integration tests', () => {
56
159
  });
57
160
  });
58
161
 
59
- describe('updateCell', () => {
60
- const testCases: [string, number, number, string][] = [
61
- ['Sheet1', 0, 0, 'First Value'],
62
- ['Sheet1', 0, 0, 'Updated Value'],
63
- ['Sheet1', 1, 1, '123'],
64
- ['Sheet1', 2, 2, 'Test'],
65
- ];
162
+ describe('createNewSheetIfNotExists', () => {
163
+ test('creates the sheet and stays idempotent on a second call', async () => {
164
+ await deleteSheetsByTitle([scratchSheetName]);
165
+ expect(await listSheetTitles()).not.toContain(scratchSheetName);
66
166
 
67
- test.each(testCases)(
68
- 'updates cell in sheet %s at row %d col %d with value %s',
69
- async (sheetName: string, row: number, col: number, value: string) => {
70
- await repository.updateCell(spreadsheetUrl, sheetName, row, col, value);
71
- const result = await repository.getSheet(spreadsheetUrl, sheetName);
72
- if (!result) {
73
- throw new Error('Sheet not found');
74
- }
75
- expect(result[row][col]).toBe(value);
76
- },
77
- );
167
+ await repository.createNewSheetIfNotExists(
168
+ spreadsheetUrl,
169
+ scratchSheetName,
170
+ );
171
+ expect(await listSheetTitles()).toContain(scratchSheetName);
172
+ expect(
173
+ await repository.getSheet(spreadsheetUrl, scratchSheetName),
174
+ ).toBeNull();
175
+
176
+ await repository.createNewSheetIfNotExists(
177
+ spreadsheetUrl,
178
+ scratchSheetName,
179
+ );
180
+ const titlesAfterSecondCall = await listSheetTitles();
181
+ expect(
182
+ titlesAfterSecondCall.filter((title) => title === scratchSheetName)
183
+ .length,
184
+ ).toBe(1);
185
+ });
78
186
  });
79
187
 
80
188
  describe('appendSheetValues', () => {
81
- const testCases: [string[][]][] = [
82
- [[['Single Row']]],
83
- [[['Multiple', 'Columns']]],
84
- [
85
- [
86
- ['Row1Col1', 'Row1Col2'],
87
- ['Row2Col1', 'Row2Col2'],
88
- ],
89
- ],
90
- ];
189
+ test('appends rows to a sheet it creates and keeps the earlier rows', async () => {
190
+ await deleteSheetsByTitle([scratchSheetName]);
191
+
192
+ await repository.appendSheetValues(spreadsheetUrl, scratchSheetName, [
193
+ ['firstRowFirstColumn', 'firstRowSecondColumn'],
194
+ ]);
195
+ expect(
196
+ await repository.getSheet(spreadsheetUrl, scratchSheetName),
197
+ ).toEqual([['firstRowFirstColumn', 'firstRowSecondColumn']]);
198
+
199
+ await repository.appendSheetValues(spreadsheetUrl, scratchSheetName, [
200
+ ['secondRowFirstColumn', 'secondRowSecondColumn'],
201
+ ['thirdRowFirstColumn', 'thirdRowSecondColumn'],
202
+ ]);
203
+ expect(
204
+ await repository.getSheet(spreadsheetUrl, scratchSheetName),
205
+ ).toEqual([
206
+ ['firstRowFirstColumn', 'firstRowSecondColumn'],
207
+ ['secondRowFirstColumn', 'secondRowSecondColumn'],
208
+ ['thirdRowFirstColumn', 'thirdRowSecondColumn'],
209
+ ]);
210
+ });
211
+ });
91
212
 
92
- test.each(testCases)(
93
- 'appends values %j to sheet',
94
- async (values: string[][]) => {
95
- const sheetName = 'AppendTest';
96
- const initialSheet = await repository.getSheet(
97
- spreadsheetUrl,
98
- sheetName,
99
- );
100
- const initialLength = initialSheet ? initialSheet.length : 0;
213
+ describe('updateCell', () => {
214
+ test('creates the sheet when missing, writes a cell, and overwrites it', async () => {
215
+ await deleteSheetsByTitle([scratchSheetName]);
101
216
 
102
- await repository.appendSheetValues(spreadsheetUrl, sheetName, values);
217
+ await repository.updateCell(
218
+ spreadsheetUrl,
219
+ scratchSheetName,
220
+ 0,
221
+ 0,
222
+ 'firstValue',
223
+ );
224
+ expect(
225
+ await repository.getSheet(spreadsheetUrl, scratchSheetName),
226
+ ).toEqual([['firstValue']]);
103
227
 
104
- const updatedSheet = await repository.getSheet(
105
- spreadsheetUrl,
106
- sheetName,
107
- );
108
- expect(updatedSheet).not.toBeNull();
109
- if (!updatedSheet) {
110
- throw new Error('Sheet not found');
111
- }
112
- expect(updatedSheet.length).toBe(initialLength + values.length);
113
-
114
- for (let i = 0; i < values.length; i++) {
115
- expect(updatedSheet[initialLength + i]).toEqual(values[i]);
116
- }
117
- },
118
- );
228
+ await repository.updateCell(
229
+ spreadsheetUrl,
230
+ scratchSheetName,
231
+ 0,
232
+ 0,
233
+ 'updatedValue',
234
+ );
235
+ expect(
236
+ await repository.getSheet(spreadsheetUrl, scratchSheetName),
237
+ ).toEqual([['updatedValue']]);
238
+
239
+ await repository.updateCell(
240
+ spreadsheetUrl,
241
+ scratchSheetName,
242
+ 2,
243
+ 2,
244
+ 'thirdRowThirdColumn',
245
+ );
246
+ const sheet = await repository.getSheet(spreadsheetUrl, scratchSheetName);
247
+ if (!sheet) {
248
+ throw new Error('Sheet not found');
249
+ }
250
+ expect(sheet[0][0]).toBe('updatedValue');
251
+ expect(sheet[2][2]).toBe('thirdRowThirdColumn');
252
+ });
119
253
  });
120
254
  });
@@ -23,11 +23,43 @@ describe('GoogleSpreadsheetRepository', () => {
23
23
  const spreadsheetUrl =
24
24
  'https://docs.google.com/spreadsheets/d/1N_3y0y46v5tHbra5YSm6PldflcsF1bkfeWDdQ3MRuXM/edit?gid=0#gid=0';
25
25
 
26
- const mockSpreadsheetsGet = jest.fn<() => Promise<SpreadsheetGetResponse>>();
27
- const mockSpreadsheetsValuesGet = jest.fn<() => Promise<ValuesGetResponse>>();
28
- const mockSpreadsheetsValuesUpdate = jest.fn<() => Promise<SimpleResponse>>();
29
- const mockSpreadsheetsValuesAppend = jest.fn<() => Promise<SimpleResponse>>();
30
- const mockSpreadsheetsBatchUpdate = jest.fn<() => Promise<SimpleResponse>>();
26
+ const mockSpreadsheetsGet =
27
+ jest.fn<
28
+ (params: { spreadsheetId: string }) => Promise<SpreadsheetGetResponse>
29
+ >();
30
+ const mockSpreadsheetsValuesGet =
31
+ jest.fn<
32
+ (params: {
33
+ spreadsheetId: string;
34
+ range: string;
35
+ }) => Promise<ValuesGetResponse>
36
+ >();
37
+ const mockSpreadsheetsValuesUpdate =
38
+ jest.fn<
39
+ (params: {
40
+ spreadsheetId: string;
41
+ range: string;
42
+ valueInputOption: string;
43
+ requestBody: { values: string[][] };
44
+ }) => Promise<SimpleResponse>
45
+ >();
46
+ const mockSpreadsheetsValuesAppend =
47
+ jest.fn<
48
+ (params: {
49
+ spreadsheetId: string;
50
+ range: string;
51
+ valueInputOption: string;
52
+ requestBody: { values: string[][] };
53
+ }) => Promise<SimpleResponse>
54
+ >();
55
+ const mockSpreadsheetsBatchUpdate = jest.fn<
56
+ (params: {
57
+ spreadsheetId: string;
58
+ requestBody: {
59
+ requests: Array<{ addSheet?: { properties?: { title?: string } } }>;
60
+ };
61
+ }) => Promise<SimpleResponse>
62
+ >();
31
63
 
32
64
  const mockSheetsClient = {
33
65
  spreadsheets: {
@@ -289,6 +321,32 @@ describe('GoogleSpreadsheetRepository', () => {
289
321
  );
290
322
  });
291
323
 
324
+ test('does not recreate an existing sheet that holds no values', async () => {
325
+ mockSpreadsheetsGet.mockResolvedValue({
326
+ status: 200,
327
+ data: { sheets: [{ properties: { title: 'EmptyExistingSheet' } }] },
328
+ });
329
+ mockSpreadsheetsValuesGet.mockResolvedValue({
330
+ status: 200,
331
+ data: {},
332
+ });
333
+ mockSpreadsheetsValuesAppend.mockResolvedValue({
334
+ status: 200,
335
+ data: {},
336
+ });
337
+
338
+ await repository.appendSheetValues(spreadsheetUrl, 'EmptyExistingSheet', [
339
+ ['Row'],
340
+ ]);
341
+
342
+ expect(mockSpreadsheetsBatchUpdate).not.toHaveBeenCalled();
343
+ expect(mockSpreadsheetsValuesAppend).toHaveBeenCalledWith(
344
+ expect.objectContaining({
345
+ range: 'EmptyExistingSheet!A1:A',
346
+ }),
347
+ );
348
+ });
349
+
292
350
  test('creates new sheet via batchUpdate when sheet does not exist', async () => {
293
351
  mockSpreadsheetsGet.mockResolvedValue({
294
352
  status: 200,
@@ -155,14 +155,29 @@ export class GoogleSpreadsheetRepository implements SpreadsheetRepository {
155
155
  );
156
156
  }
157
157
  };
158
+ private sheetExists = async (
159
+ spreadsheetUrl: string,
160
+ sheetName: string,
161
+ ): Promise<boolean> => {
162
+ const sheets = this.sheetsClient;
163
+ const spreadsheetId = this.getSpreadsheetId(spreadsheetUrl);
164
+ const response = await sheets.spreadsheets.get({ spreadsheetId });
165
+ if (response.status !== 200) {
166
+ throw new Error(
167
+ `Failed to get sheet: ${response.status}. ${JSON.stringify(response.data)}`,
168
+ );
169
+ }
170
+ return (response.data.sheets ?? []).some(
171
+ (sheet) => sheet.properties?.title === sheetName,
172
+ );
173
+ };
158
174
  createNewSheetIfNotExists = async (
159
175
  spreadsheetUrl: string,
160
176
  sheetName: string,
161
177
  ): Promise<void> => {
162
178
  const sheets = this.sheetsClient;
163
179
  const spreadsheetId = this.getSpreadsheetId(spreadsheetUrl);
164
- const sheet = await this.getSheet(spreadsheetUrl, sheetName);
165
- if (sheet !== null) {
180
+ if (await this.sheetExists(spreadsheetUrl, sheetName)) {
166
181
  return;
167
182
  }
168
183
  const response = await sheets.spreadsheets.batchUpdate({
@@ -132,7 +132,7 @@ describe('LocalStorageRepository', () => {
132
132
  testCases.forEach(({ name, params }) => {
133
133
  test(name, () => {
134
134
  if (fs.existsSync(params[0])) {
135
- fs.rmdirSync(params[0], { recursive: true });
135
+ fs.rmSync(params[0], { recursive: true });
136
136
  }
137
137
  repository.mkdir(...params);
138
138
  expect(fs.existsSync(params[0])).toBe(true);
@@ -72,6 +72,7 @@ export declare class GoogleSpreadsheetRepository implements SpreadsheetRepositor
72
72
  getSpreadsheetId: (spreadsheetUrl: string) => string;
73
73
  getSheet: (spreadsheetUrl: string, sheetName: string) => Promise<string[][] | null>;
74
74
  updateCell: (spreadsheetUrl: string, sheetName: string, row: number, column: number, value: string) => Promise<void>;
75
+ private sheetExists;
75
76
  createNewSheetIfNotExists: (spreadsheetUrl: string, sheetName: string) => Promise<void>;
76
77
  appendSheetValues: (spreadsheetUrl: string, sheetName: string, values: string[][]) => Promise<void>;
77
78
  }
@@ -1 +1 @@
1
- {"version":3,"file":"GoogleSpreadsheetRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/GoogleSpreadsheetRepository.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GoogleSpreadsheetRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/GoogleSpreadsheetRepository.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- /*! tailwindcss v4.3.1 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-font-weight:initial;--tw-outline-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-medium:500;--font-weight-semibold:600;--radius-md:.375rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-input:#e5e5e5;--color-ring:#0a0a0a;--color-background:#fff;--color-foreground:#0a0a0a;--color-primary:#171717;--color-primary-foreground:#fafafa;--color-secondary:#f5f5f5;--color-secondary-foreground:#171717;--color-accent:#f5f5f5;--color-accent-foreground:#171717}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.relative{position:relative}.static{position:static}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.block{display:block}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.items-center{align-items:center}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-input{border-color:var(--color-input)}.border-transparent{border-color:#0000}.bg-background{background-color:var(--color-background)}.bg-primary{background-color:var(--color-primary)}.bg-secondary{background-color:var(--color-secondary)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-2{padding-block:calc(var(--spacing) * 2)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.whitespace-nowrap{white-space:nowrap}.text-foreground{color:var(--color-foreground)}.text-primary-foreground{color:var(--color-primary-foreground)}.text-secondary-foreground{color:var(--color-secondary-foreground)}.lowercase{text-transform:lowercase}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.hover\:bg-accent:hover{background-color:var(--color-accent)}.hover\:bg-primary\/90:hover{background-color:#171717e6}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--color-primary) 90%,transparent)}}.hover\:bg-secondary\/80:hover{background-color:#f5f5f5cc}@supports (color:color-mix(in lab,red,red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab,var(--color-secondary) 80%,transparent)}}.hover\:text-accent-foreground:hover{color:var(--color-accent-foreground)}}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:var(--color-ring)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}}html{font-size:clamp(15px,2.1vw,26px)}body{color:#e6edf3;background-color:#0d1117;margin:0;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif}.console-app{flex-direction:column;width:100%;min-height:100dvh;display:flex}.console-tabbar{-webkit-overflow-scrolling:touch;background:#161b22;border-bottom:2px solid #30363d;flex-wrap:nowrap;align-items:stretch;gap:0;min-height:42px;padding:0 8px;display:flex;overflow-x:auto}.console-tab{color:#8b949e;white-space:nowrap;cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;align-items:center;gap:6px;margin-bottom:-2px;padding:10px 14px;font-size:.8125rem;font-weight:500;line-height:1.2;text-decoration:none;display:inline-flex}.console-tab:hover{color:#e6edf3}.console-tab[data-active=true]{color:#e6edf3;border-bottom-color:#2f81f7;font-weight:700}.console-tab-badge{text-align:center;color:#e6edf3;background:#484f58;border-radius:20px;min-width:20px;padding:1px 7px;font-size:.6875rem;font-weight:700;line-height:1.5}.console-tab-badge[data-zero=true]{color:#8b949e;background:#30363d}.console-tab-pjname{color:#8b949e;align-self:center;margin-left:auto;padding:0 8px;font-size:.7188rem}.console-tab-geninfo{color:#8b949e;align-self:center;padding:0 4px;font-size:.6875rem}.console-list{margin:0;padding:12px 18px 18px;list-style:none}.console-list-group{list-style:none}.console-item-row .console-item-icon{flex:none;margin-top:3px}.console-group-header{background:#0b0f14;border-bottom:1px solid #21262d;justify-content:space-between;align-items:center;padding:6px 12px;display:flex}.console-storytag{align-items:center;gap:8px;font-size:.8125rem;font-weight:700;display:inline-flex}.console-story-dot{border-radius:999px;width:10px;height:10px;display:inline-block}.console-group-count{color:#8b949e;font-size:.75rem}.console-item-row{color:#e6edf3;text-align:left;cursor:pointer;background:#161b22;border:1px solid #30363d;border-radius:8px;align-items:flex-start;gap:14px;width:100%;margin-bottom:10px;padding:12px 16px;display:flex}.console-item-row:hover{background:#1a2029;border-color:#484f58}.console-item-row[data-active=true]{background:#1a2029;border-color:#4493f8}.console-item-meta{flex:1;min-width:0}.console-item-title{font-size:.9063rem;font-weight:600;display:block}.console-item-sub{color:#8b949e;margin-top:3px;font-size:.7813rem;display:block}.console-item-pill{color:#8b949e;border:1px solid #30363d;border-radius:20px;margin-right:6px;padding:1px 8px;font-size:.6875rem;display:inline-block}.console-item-createdat{color:#8b949e;cursor:help}.console-item-fields{color:#adbac7;flex-wrap:wrap;gap:4px 10px;margin-top:4px;font-size:.7188rem;display:flex}.console-item-field{word-break:break-all;align-items:baseline;gap:4px;min-width:0;display:inline-flex}.console-item-field-label{color:#6e7681;text-transform:uppercase;letter-spacing:.03em;font-size:.625rem}.console-list-message{color:#8b949e;padding:16px;font-size:.875rem}.console-list-empty{text-align:center;color:#8b949e;padding:40px;font-size:.875rem}.console-list-error,.console-comment-error,.console-files-error,.console-commits-error,.console-detail-body-error{color:#f85149}.console-detail{flex-direction:column;flex:1 0 auto;gap:12px;padding:16px 16px 0;display:flex}.console-detail-title{align-items:center;gap:8px;margin:0;font-size:1.25rem;display:flex}.console-detail-title-text{flex:1}.console-detail-number{color:#8b949e;font-weight:400}.console-detail-closed-label{color:#a371f7;font-size:.8125rem}.console-detail-subbar{align-items:center;gap:12px;font-size:.8125rem;display:flex}.console-detail-link{color:#4493f8}.console-detail-repo{color:#8b949e;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.console-copy-url-button{color:#c9d1d9;border:1px solid #30363d;border-radius:6px;height:24px;padding:0 8px;font-size:.75rem}.console-copy-url-button:hover{color:#e6edf3;background-color:#21262d}.console-detail-pill,.console-label-chip,.console-detail-status-chip{border:1px solid #30363d;border-radius:999px;padding:2px 8px;font-size:.75rem;display:inline-block}.console-detail-pr-status-row{flex-wrap:wrap;align-items:center;gap:6px;margin-top:-4px;display:flex}.console-detail-pr-status{flex-wrap:wrap;align-items:center;gap:6px;display:inline-flex}.console-detail-ci-missing{opacity:.85;font-weight:400}.console-detail-mergeable-chip{font-weight:600}.console-detail-mergeable-chip-unknown{opacity:.7;font-weight:400}.console-detail-labels{flex-wrap:wrap;gap:6px;display:flex}.console-detail-createdat{color:#8b949e;font-size:.75rem}.console-panel{border:1px solid #30363d;border-radius:8px;overflow:hidden}.console-panel-header{background:#161b22;justify-content:space-between;align-items:center;display:flex}.console-panel-toggle{color:#e6edf3;text-align:left;cursor:pointer;background:0 0;border:none;flex:1;align-items:center;gap:8px;padding:6px 12px;font-size:.875rem;font-weight:600;display:flex}.console-panel-toggle:hover{background:#1a2029}.console-panel-action{padding:6px 12px}.console-panel-body{padding:12px}.console-markdown{word-break:break-word;font-size:.875rem;line-height:1.5}.console-markdown ul{margin:.5em 0;padding-left:1.5em;list-style:outside}.console-markdown ol{margin:.5em 0;padding-left:1.5em;list-style:decimal}.console-markdown li{margin:.25em 0}.console-markdown table{border-collapse:collapse;margin:.5em 0}.console-markdown th,.console-markdown td{border:1px solid #30363d;padding:4px 8px}.console-markdown th{background:#161b22}.console-markdown h1,.console-markdown h2,.console-markdown h3,.console-markdown h4,.console-markdown h5,.console-markdown h6{margin:1em 0 .5em;font-weight:700;line-height:1.25}.console-markdown h1{font-size:1.6em}.console-markdown h2{font-size:1.4em}.console-markdown h3{font-size:1.2em}.console-markdown h4{font-size:1.05em}.console-markdown h5{font-size:.95em}.console-markdown h6{color:#8b949e;font-size:.9em}.console-markdown pre{background:#161b22;border:1px solid #30363d;border-radius:6px;margin:.5em 0;padding:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8125rem;line-height:1.45;overflow-x:auto}.console-markdown code{background:#6e768166;border-radius:4px;padding:.15em .4em;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em}.console-markdown pre code{font-size:inherit;background:0 0;border-radius:0;padding:0}.console-markdown blockquote{color:#8b949e;border-left:3px solid #30363d;margin:.5em 0;padding:0 1em}.console-markdown a{color:#4493f8;text-decoration:none}.console-markdown a:hover{text-decoration:underline}.console-markdown-reference-host{display:inline}.console-markdown-reference{vertical-align:baseline;align-items:center;gap:.25em;display:inline-flex}.console-markdown-reference .console-item-icon{flex-shrink:0}.console-markdown-reference-title{text-decoration:none}.console-markdown-reference:hover .console-markdown-reference-title{text-decoration:underline}.console-markdown hr{border:none;border-top:1px solid #30363d;margin:1em 0}.console-mermaid-error{color:#f85149;font-size:.8125rem}.console-comment{background:#161b22;border:1px solid #30363d;border-radius:8px;margin-bottom:10px;padding:12px 16px}.console-comment-header{color:#8b949e;gap:8px;font-size:.75rem;display:flex}.console-comment-show-all{color:#e6edf3;cursor:pointer;background:#21262d;border:1px solid #30363d;border-radius:6px;margin-bottom:10px;padding:6px 12px;font-size:.75rem;font-weight:600}.console-comment-show-all:hover{background-color:#262c34;border-color:#484f58}.console-comment-author{color:#e6edf3;font-weight:600}.console-files,.console-commits{margin:0;padding:0;list-style:none}.console-commit{align-items:center;gap:8px;padding:4px 0;font-size:.8125rem;display:flex}.console-file{flex-direction:column;font-size:.8125rem;display:flex}.console-file-tree,.console-file-tree-children{margin:0;padding:0;list-style:none}.console-file-tree-dir{flex-direction:column;display:flex}.console-file-tree-dir-name{color:#8b949e;align-items:center;gap:6px;padding:4px 0;font-weight:600;display:flex}.console-file-tree-dir-icon{flex:none}.console-file-badge{text-align:center;border:1px solid;border-radius:4px;width:18px;font-size:.6875rem}.console-file-path,.console-commit-message{flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.console-file-add,.console-pr-add{color:#3fb950}.console-file-del,.console-pr-del{color:#f85149}.console-commit-sha{color:#8b949e;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.console-actionbar{z-index:100;padding:10px 16px;padding-bottom:calc(10px + env(safe-area-inset-bottom));background:#161b22;border-top:2px solid #30363d;margin-top:auto;position:sticky;bottom:0}.console-operation-bar{flex-direction:column;gap:8px;width:100%;display:flex}.console-op-group{flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:8px;display:flex}.console-op-group-review{gap:10px}.console-op-group-stories{max-height:33vh;overflow-y:auto}.console-op-button{color:#e6edf3;white-space:nowrap;cursor:pointer;background:#21262d;border:1px solid #30363d;border-radius:6px;padding:9px 16px;font-size:.8125rem;font-weight:600}.console-op-button:hover{border-color:#484f58}.console-op-button-approve{color:#fff;background:#238636;border-color:#2ea043}.console-op-button-reject{color:#ffd166;background:#7d5000;border-color:#a06800}.console-op-button-wrong{color:#f85149;background:#3a1518;border-color:#f85149}.console-op-button-unneeded{color:#aab0b8;background:#2a2d31;border-color:#6e7681}.console-op-button-snooze{color:#79c0ff;background:#1c2b4a;border-color:#4493f8}.console-pr-section{border:1px solid #30363d;border-radius:8px;flex-direction:column;gap:10px;padding:12px;display:flex}.console-pr-statbar{color:#8b949e;gap:12px;font-size:.75rem;display:flex}.console-detail-screen{flex-direction:column;flex:1 0 auto;display:flex}.console-panel-open-link{color:#4493f8;font-size:.8125rem;font-weight:400}.console-composer{margin-top:4px}.console-composer-toggle{color:#8b949e;cursor:pointer;background:0 0;border:none;padding:2px 0;font-size:.7813rem}.console-composer-toggle:hover{color:#e6edf3}.console-composer-posted{flex-direction:column;gap:8px;margin-top:8px;display:flex}.console-composer-form{margin-top:8px}.console-composer-input{box-sizing:border-box;color:#e6edf3;width:100%;font:inherit;resize:vertical;background:#21262d;border:1px solid #30363d;border-radius:6px;padding:8px;font-size:.875rem}.console-composer-row{justify-content:flex-end;align-items:center;gap:8px;margin-top:6px;display:flex}.console-composer-submit{color:#fff;cursor:pointer;background:#238636;border:1px solid #2ea043;border-radius:6px;padding:7px 16px;font-size:.8125rem;font-weight:600}.console-composer-submit:disabled{opacity:.6;cursor:default}.console-composer-status{color:#8b949e;font-size:.75rem}.console-composer-error{color:#f85149}.console-undo-toast{z-index:9998;white-space:nowrap;border-radius:10px;align-items:center;gap:12px;max-width:92vw;padding:12px 18px;font-size:.8438rem;display:flex;position:fixed;top:12px;left:50%;transform:translate(-50%);box-shadow:0 4px 20px #0009}.console-undo-toast-green{color:#3fb950;background:#0d3320;border:1px solid #2ea043}.console-undo-toast-amber{color:#ffd166;background:#3a2800;border:1px solid #a06800}.console-undo-toast-red{color:#f85149;background:#3a1518;border:1px solid #da3633}.console-undo-toast-gray{color:#aab0b8;background:#2a2d31;border:1px solid #6e7681}.console-undo-toast-blue{color:#79c0ff;background:#0d1f40;border:1px solid #4493f8}.console-undo-toast-error{color:#f85149;background:#3a1518;border:1px solid #f85149}.console-undo-toast-message{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.console-undo-toast-undo{cursor:pointer;color:inherit;background:#ffffff1f;border:none;border-radius:6px;flex:none;padding:4px 12px;font-size:.8125rem;font-weight:700}.console-undo-toast-undo:hover{background:#ffffff38}.console-undo-toast-countdown{opacity:.7;flex:none;font-size:.6875rem}.console-undo-toast-bar{opacity:.5;background:currentColor;border-radius:0 0 10px 10px;height:3px;transition:width .1s linear;position:absolute;bottom:0;left:0}.console-file-row{width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;padding:4px 0;font-size:.8125rem;display:flex}.console-file-row:hover{background:#1a2029}.console-file-caret{color:#8b949e;flex:none;width:1em}.console-file-diff{border-collapse:collapse;width:100%;margin:4px 0 8px;font:.8125rem/1.6 ui-monospace,SFMono-Regular,Menlo,monospace}.console-file-diff td{white-space:pre-wrap;word-break:break-word;vertical-align:top;padding:0 10px}.console-diff-ln{text-align:right;color:#6e7681;-webkit-user-select:none;user-select:none;white-space:nowrap;border-right:1px solid #30363d;width:1%;padding:0 8px}.console-file-diff td.console-diff-ln{white-space:nowrap;word-break:normal;overflow-wrap:normal}.console-diff-add td{background:#2ea04326}.console-diff-add .console-diff-code{color:#aff5b4}.console-diff-del td{background:#f8514926}.console-diff-del .console-diff-code{color:#ffdcd7}.console-diff-hunk td{color:#79c0ff;background:#161b22}.console-diff-ctx .console-diff-code{color:#c9d1d9}.console-file-diff-empty{color:#8b949e;padding:8px 14px;font-size:.75rem}.console-diff-comment-cell{text-align:center;-webkit-user-select:none;user-select:none;width:1%;padding:0 2px}.console-diff-comment-button{color:#6e7681;cursor:pointer;opacity:.6;background:0 0;border:1px solid #30363d;border-radius:4px;width:18px;height:18px;padding:0;font-size:.875rem;line-height:16px}.console-diff-row:hover .console-diff-comment-button,.console-diff-comment-button:focus-visible,.console-diff-comment-button[aria-expanded=true]{opacity:1;color:#fff;background:#1f6feb;border-color:#2f81f7}.console-diff-composer-row td{background:#0d1117;padding:6px 10px}.console-diff-composer{background:#161b22;border:1px solid #30363d;border-radius:6px;flex-direction:column;gap:6px;padding:8px;display:flex}.console-diff-composer-anchor{color:#8b949e;font-size:.6875rem}.console-diff-composer-input{resize:vertical;color:#c9d1d9;width:100%;font:inherit;background:#0d1117;border:1px solid #30363d;border-radius:6px;padding:6px 8px}.console-diff-composer-controls{align-items:center;gap:8px;display:flex}.console-diff-composer-submit{color:#fff;cursor:pointer;background:#238636;border:1px solid #238636;border-radius:6px;padding:4px 12px}.console-diff-composer-submit:disabled{opacity:.6;cursor:default}.console-diff-composer-cancel{color:#c9d1d9;cursor:pointer;background:0 0;border:1px solid #30363d;border-radius:6px;padding:4px 12px}.console-diff-composer-status{color:#8b949e;font-size:.75rem}.console-diff-composer-error{color:#ff7b72}.console-diff-composer-posted{color:#3fb950;margin:0;font-size:.75rem}.console-pr-header{flex-wrap:wrap;align-items:center;gap:10px;padding-top:4px;display:flex}.console-pr-section-title{color:#e6edf3;font-size:.9375rem;font-weight:600}.console-pr-section-state{color:#8b949e;border:1px solid #6e7681;border-radius:999px;padding:1px 8px;font-size:.6875rem}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}