texlite 0.4.1 → 0.5.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.
- package/dist/client/assets/BinaryModule-Aj1vLnwf-CqJDurFy.js +4 -0
- package/dist/client/assets/{GitDialog-Cf5wW5Wn.js → GitDialog-BkY6SFSC.js} +1 -1
- package/dist/client/assets/{HistoryDialog-CXb-MzXH.js → HistoryDialog-BwGvEITC.js} +1 -1
- package/dist/client/assets/LatexEditor-CbjDmoi7.js +59 -0
- package/dist/client/assets/{PdfPreview-YbXIaedo.js → PdfPreview-KsQWdlGK.js} +1 -1
- package/dist/client/assets/{ProjectNavigationDialogs-BMz-OL3a.js → ProjectNavigationDialogs-DmHrXhIU.js} +1 -1
- package/dist/client/assets/{SystemMetricsDialog-BLfDSaDc.js → SystemMetricsDialog-CAJqAWmA.js} +1 -1
- package/dist/client/assets/__vite-browser-external-BIHI7g3E.js +1 -0
- package/dist/client/assets/binary-DhyLq3C-.js +1 -0
- package/dist/client/assets/harper_wasm_bg-LWXDb7Ll.wasm +0 -0
- package/dist/client/assets/index-BDbf4yIq.js +3456 -0
- package/dist/client/assets/index-BLh_AgbG.js +17 -0
- package/dist/client/assets/index-bVUcyCfK.css +1 -0
- package/dist/client/assets/{plus-CvyFTTaw.js → plus-CymJUapL.js} +1 -1
- package/dist/client/assets/spellCheck-BDmECWDN.js +5 -0
- package/dist/client/index.html +2 -2
- package/dist/server/compiler.js +49 -1
- package/dist/server/latexCompletion.js +30 -7
- package/dist/server/routes/compile.js +50 -2
- package/package.json +2 -3
- package/dist/client/assets/LatexEditor-ytqaJMkb.js +0 -61
- package/dist/client/assets/index-BI4dns2b.js +0 -17
- package/dist/client/assets/index-Ddch-kkF.css +0 -1
- package/dist/client/assets/spellCheck-ClNG2x_t.js +0 -49783
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;color:#202421;background:#f3f4f1;font-synthesis:none}*{box-sizing:border-box}html,body,#root{margin:0;min-height:100%}button,input,select,textarea{font:inherit}button{cursor:pointer}button:disabled{cursor:not-allowed;opacity:.45}.muted{color:#737a75}.error{color:#b3261e;font-size:14px}.dialog-error{margin:0;padding:8px 10px;border:1px solid #e9b6b0;border-radius:6px;background:#fff3f1}.field-hint{color:#778179;font-size:11px;font-weight:400}.primary,.compile{border:0;border-radius:8px;background:#176b44;color:#fff;padding:10px 18px;font-weight:650}.icon-button{display:inline-flex;align-items:center;gap:6px}.ghost{border:0;background:transparent;color:inherit}.page{min-height:100vh;display:flex;flex-direction:column}.login-page{min-height:100vh;display:grid;grid-template-rows:1fr auto;place-items:center;padding-top:44px;background:radial-gradient(circle at 50% 0,#d8e8dd,#f5f5f1 45%)}.login-card{width:min(390px,calc(100vw - 32px));padding:38px;display:flex;flex-direction:column;gap:16px;background:#ffffffeb;border:1px solid #dfe4df;border-radius:16px;box-shadow:0 24px 70px #1d31241f}.login-card h1{text-align:center;margin:0;letter-spacing:-.04em}.login-card>p{text-align:center;margin:-8px 0 8px}.login-card label,.settings label{display:flex;flex-direction:column;gap:7px;font-size:13px;font-weight:650}.login-card input,.settings input,.settings select,.settings textarea{width:100%;border:1px solid #cbd2cc;border-radius:8px;padding:10px 11px;background:#fff;outline:none}.login-card input:focus,.settings input:focus{border-color:#176b44;box-shadow:0 0 0 3px #176b4420}.site-logo{width:40px;height:40px;flex:none;display:inline-flex;align-items:center;justify-content:center}.site-logo img{display:block;width:100%;height:100%}.site-logo.compact{width:34px;height:34px;margin:0 16px 0 10px}.site-logo.auth-logo{width:76px;height:76px;margin:0 auto 2px;filter:drop-shadow(0 10px 18px #1e2a4a25)}.site-title{margin-right:11px;font-size:20px;font-weight:800;letter-spacing:-.04em}.sr-only{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.support{color:#737a75;text-align:center}.topbar,.editor-topbar{height:62px;display:flex;align-items:center;padding:0 26px;background:#123c2a;color:#fff}.top-actions{margin-left:auto;display:flex;align-items:center;gap:18px;font-size:14px}.top-actions .ghost{display:inline-flex;align-items:center;gap:5px}.top-user-identity{min-width:0;display:flex;flex-direction:column;line-height:1.15}.top-user-identity strong{overflow:hidden;max-width:180px;font-size:13px;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.top-user-identity small{overflow:hidden;max-width:180px;margin-top:3px;color:#b9d0c2;font-size:10px;text-overflow:ellipsis;white-space:nowrap}.language-switcher{position:fixed;top:18px;right:20px;z-index:5;display:flex;align-items:center;gap:6px;color:#536059;font-size:12px}.language-switcher select{border:1px solid #c9d0ca;border-radius:6px;padding:5px 7px;background:#fff;color:#26342b}.language-switcher.compact{position:static;color:inherit}.language-switcher.compact select{border-color:#ffffff35;background:#ffffff10;color:inherit}.language-switcher.compact option{color:#202421}.dashboard{width:100%;max-width:1180px;flex:1;margin:0 auto;padding:52px 34px}.site-footer{width:100%;display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:7px 16px;padding:18px 24px;border-top:1px solid #dfe3df;background:#eef0ed;color:#737b75;font-size:11px;text-align:center}.site-footer a{color:#176b44;text-decoration:none}.site-footer a:hover{text-decoration:underline}.login-page>.site-footer{align-self:end;background:#ffffff50}.section-title{display:flex;justify-content:space-between;align-items:end;margin-bottom:28px}.section-title h1{display:flex;align-items:center;gap:9px;margin:0;font-size:30px;letter-spacing:-.04em}.section-title h1 svg{flex:none;color:#315b43}.section-title p{margin:7px 0 0}.section-actions{display:flex;gap:9px}.section-actions>button{display:inline-flex;align-items:center;gap:6px;border:1px solid #bfc8c1;border-radius:8px;background:#fff;padding:10px 16px}.section-actions>.primary{border-color:#176b44;background:#176b44;color:#fff}.project-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:12px;margin-bottom:18px;padding:10px;background:#e9ece8;border-radius:10px}.project-toolbar>input{width:220px;border:1px solid #cbd2cc;border-radius:7px;padding:8px 10px;background:#fff}.project-scope{display:flex;flex:none;gap:2px}.project-scope button{display:flex;align-items:center;gap:5px;flex:none;border:1px solid transparent;border-radius:7px;padding:7px 9px;background:transparent;color:#536059;font-size:12px}.project-scope button.active{border-color:#b8c4ba;background:#fff;color:#174b33}.tag-filters{min-width:0;flex:1;display:flex;gap:6px;overflow-x:auto}.tag-filters button,.view-toggle button{display:flex;align-items:center;gap:5px;flex:none;border:1px solid transparent;border-radius:7px;padding:7px 9px;background:transparent;color:#536059;font-size:12px}.tag-filters button.active,.view-toggle button.active{border-color:#b8c4ba;background:#fff;color:#174b33}.project-sort{flex:none;display:flex;align-items:center;gap:5px;color:#536059;font-size:11px}.project-sort>span{white-space:nowrap}.project-sort select{max-width:150px;border:1px solid #c4ccc6;border-radius:7px;padding:7px 25px 7px 8px;background:#fff;color:#34433a;font-size:12px}.view-toggle{display:flex;gap:2px}.project-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(270px,1fr));gap:16px}.project-card{position:relative;min-height:250px;display:flex;flex-direction:column;text-align:left;background:#fff;border:1px solid #dfe2de;border-radius:12px;overflow:hidden;transition:.15s ease}.project-card:hover{border-color:#8bae9a;transform:translateY(-2px);box-shadow:0 10px 30px #25352a10}.project-card-open{all:unset;box-sizing:border-box;min-width:0;flex:1;display:flex;flex-direction:column;align-items:flex-start;gap:10px;padding:20px 20px 14px;cursor:pointer}.project-card-open:focus-visible{outline:3px solid #176b4455;outline-offset:-3px}.project-card strong{font-size:17px}.project-card-main{min-width:0;display:flex;flex-direction:column;gap:7px}.project-tags,.settings-tags{display:flex;flex-wrap:wrap;gap:5px}.owner-badge{max-width:100%;overflow:hidden;padding:6px 9px;border-radius:7px;background:#e2f0e7;color:#176b44;font-size:11px;font-weight:750;text-overflow:ellipsis;white-space:nowrap}.project-grid:not(.list-view) .owned-project .owner-badge{max-width:calc(100% - 94px)}.project-transfer-action{position:absolute;z-index:2;top:17px;right:16px;display:inline-flex;align-items:center;gap:4px;border:1px solid #cbd7cf;border-radius:6px;padding:5px 7px;background:#f7faf8;color:#315c43;font-size:10px}.project-transfer-action:hover{border-color:#8caf99;background:#e8f1eb;color:#176b44}.project-meta{width:100%;display:grid;grid-template-columns:1fr;gap:8px;margin:auto 0 0}.project-meta div{min-width:0}.project-meta dt{display:flex;align-items:center;gap:4px;color:#8a918c;font-size:10px;text-transform:uppercase}.project-meta dt svg{flex:none;color:#6f8e7a}.project-meta dd{overflow:hidden;margin:2px 0 0;color:#555e58;font-size:11px;text-overflow:ellipsis;white-space:nowrap}.project-grid:not(.list-view) .project-meta dd{overflow:visible;text-overflow:clip;white-space:normal;overflow-wrap:anywhere}.project-modified-by{color:#68716b}.project-card-actions{display:flex;flex-wrap:wrap;gap:5px;padding:9px 12px;border-top:1px solid #eceeeb;background:#fafbf9}.project-card-actions button,.project-card-actions a{display:inline-flex;align-items:center;gap:5px;border:0;border-radius:5px;padding:5px 7px;background:transparent;color:#4f5b53;font-size:11px;text-decoration:none}.project-card-actions button:hover,.project-card-actions a:hover{background:#e8eeea;color:#176b44}.project-card-actions .danger-text{margin-left:auto;color:#b3261e}.project-grid:not(.list-view) .project-card-actions{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:4px}.project-grid:not(.list-view) .project-card-actions button,.project-grid:not(.list-view) .project-card-actions a{width:100%;min-width:0;justify-content:flex-start;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.project-grid:not(.list-view) .project-card-actions .danger-text{margin-left:0}.project-grid.list-view{display:flex;flex-direction:column;gap:7px}.project-grid.list-view .project-card{min-height:88px;flex-direction:row;align-items:stretch}.project-grid.list-view .project-card-open{display:grid;grid-template-columns:minmax(90px,140px) minmax(170px,1fr) minmax(390px,2fr);align-items:center;gap:14px;padding:12px 15px}.project-grid.list-view .project-card-main{min-width:170px}.project-grid.list-view .project-meta{grid-template-columns:minmax(130px,.8fr) minmax(220px,1.2fr);gap:8px 12px;margin:0}.project-grid.list-view .project-card-actions{width:190px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-content:center;gap:4px;border-top:0;border-left:1px solid #eceeeb}.project-grid.list-view .project-card-actions button,.project-grid.list-view .project-card-actions a{width:100%;justify-content:flex-start}.project-grid.list-view .project-card-actions .danger-text{margin-left:0}.tag{display:inline-flex;align-items:center;gap:4px;width:fit-content;border-radius:999px;padding:2px 7px;background:#eef0ee;color:#4a524c;font-size:10px;font-weight:650}.tag-dot{display:inline-block;width:9px;height:9px;border-radius:50%;background:currentColor}.tag-red{--tag-color: #e04b45}.tag-orange{--tag-color: #ef9135}.tag-yellow{--tag-color: #dbb62f}.tag-green{--tag-color: #49a563}.tag-blue{--tag-color: #4c83db}.tag-purple{--tag-color: #9566ce}.tag-gray{--tag-color: #929994}.tag-dot[class*=tag-]{color:var(--tag-color)}.tag[class*=tag-]{border:1px solid color-mix(in srgb,var(--tag-color) 35%,transparent);background:color-mix(in srgb,var(--tag-color) 12%,white)}.empty,.center-card{padding:50px;color:#737a75;text-align:center}.project-empty{grid-column:1 / -1;width:100%;min-height:320px;display:flex;align-items:center;justify-content:center;flex-direction:column;box-sizing:border-box;padding:64px 24px;border:1px dashed #cbd7ce;border-radius:14px;background:linear-gradient(145deg,#fbfcfa,#f2f7f3);text-align:center}.project-empty-icon{width:58px;height:58px;display:grid;place-items:center;margin-bottom:18px;border-radius:18px;background:#dfeee4;color:#176b44}.project-empty h2{margin:0;color:#203f2e;font-size:clamp(27px,3vw,38px);line-height:1.15;letter-spacing:-.035em}.project-empty p{max-width:520px;margin:13px 0 0;color:#6e7b72;font-size:15px;line-height:1.6}.project-pagination{display:flex;align-items:center;justify-content:center;gap:12px;margin:22px 0 4px;color:#66726a;font-size:12px}.project-pagination button{display:inline-flex;align-items:center;gap:5px;border:1px solid #cbd4ce;border-radius:7px;padding:7px 10px;background:#fff;color:#405248;font-size:12px}.project-pagination button:hover:not(:disabled){border-color:#8bae9a;background:#f2f7f3;color:#176b44}.table-card{overflow:hidden;background:#fff;border:1px solid #dfe2de;border-radius:12px}table{width:100%;border-collapse:collapse}th,td{padding:14px 18px;border-bottom:1px solid #eceeeb;text-align:left;font-size:14px}th{background:#f8f9f7;color:#667069;font-size:12px;text-transform:uppercase}td small{display:block;color:#7d837f;margin-top:3px}td button{margin-right:8px;border:1px solid #cdd2ce;background:#fff;border-radius:5px;padding:5px 9px}.danger-text{color:#b3261e}.workspace{height:100vh;display:flex;flex-direction:column;overflow:hidden}.editor-topbar{height:56px;padding:0 14px;flex:none}.back{color:#fff;border:0;background:#ffffff10;width:32px;height:32px;display:grid;place-items:center;border-radius:6px}.project-heading{display:flex;flex-direction:column;min-width:0;margin-right:auto}.project-heading small{color:#b9d0c2;font-size:11px;margin-top:2px}.vim-status-badge{display:inline-flex;align-items:center;gap:5px;margin-right:8px;border:1px solid #7fd39e80;border-radius:999px;padding:5px 9px;background:#1d6b4638;color:#d7ffe3;font-size:10px;font-weight:750;letter-spacing:.04em;white-space:nowrap}.vim-status-badge svg{color:#9df0b5}.collaboration-presence{min-width:0;display:flex;align-items:center;gap:7px;padding-left:8px}.collaboration-status-dot{width:7px;height:7px;flex:none;border-radius:50%;background:#efb84b;box-shadow:0 0 0 2px #efb84b2e}.collaboration-connected .collaboration-status-dot{background:#69d391;box-shadow:0 0 0 2px #69d3912e}.collaboration-disconnected .collaboration-status-dot{background:#e07167;box-shadow:0 0 0 2px #e071672e}.collaboration-avatars{display:flex;align-items:center;padding-left:8px}.collaboration-avatar{--session-color: #1677c8;position:relative;width:28px;height:28px;display:grid;place-items:center;margin-left:-8px;border:2px solid #214d35;border-radius:50%;background:var(--session-color);color:#fff;font-size:11px;font-weight:750;cursor:default;box-shadow:0 1px 4px #061c1145}.collaboration-avatar.editing:after{content:"";position:absolute;right:-2px;bottom:-2px;width:7px;height:7px;border:2px solid #214d35;border-radius:50%;background:#8ff0af}.collaboration-avatar.local{outline:1px solid #ffffff8a;outline-offset:-4px}.collaboration-avatar-overflow{appearance:none;padding:0;border-color:#72877b;background:#f4f7f5;color:#284536;font-family:inherit;font-size:10px;cursor:pointer}.collaboration-avatar-overflow:hover,.collaboration-avatar-overflow:focus-visible{background:#dfe9e3;outline:2px solid #9bc6aa;outline-offset:1px}.collaboration-avatar-tooltip{position:absolute;z-index:80;top:calc(100% + 9px);right:-8px;width:max-content;max-width:280px;display:none;gap:2px;padding:9px 11px;border:1px solid #d7ded9;border-radius:7px;background:#fff;color:#20352a;font-size:12px;font-weight:400;line-height:1.35;text-align:left;box-shadow:0 8px 24px #061c1130}.collaboration-avatar-tooltip:before{content:"";position:absolute;top:-5px;right:15px;width:8px;height:8px;transform:rotate(45deg);border-left:1px solid #d7ded9;border-top:1px solid #d7ded9;background:#fff}.collaboration-avatar-tooltip strong,.collaboration-avatar-tooltip span,.collaboration-avatar-tooltip small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.collaboration-avatar-tooltip strong{font-size:13px}.collaboration-avatar-tooltip span{color:#526158}.collaboration-avatar-tooltip small{margin-top:3px;color:#77837b;font-size:11px}.collaboration-avatar:hover .collaboration-avatar-tooltip,.collaboration-avatar:focus-visible .collaboration-avatar-tooltip{display:block}.collaboration-recovery{min-width:0;display:inline-flex;align-items:center;gap:7px;margin:0 8px;border:1px solid #e0716780;border-radius:999px;padding:4px 6px 4px 9px;background:#6f2b2b66;color:#ffe0dd;font-size:10px;white-space:nowrap}.collaboration-recovery button{border:1px solid #ffffff55;border-radius:999px;padding:3px 7px;background:#ffffff18;color:inherit;font-size:10px}.collaboration-recovery button:hover{background:#ffffff2c}.compile-status-strip{min-height:31px;display:flex;align-items:center;justify-content:center;gap:7px;padding:6px 14px;background:#fff6dc;color:#72530d;border-bottom:1px solid #e8cd7d;font-size:11px}.compile-status-strip.failed{background:#fff0ee;color:#9a3028;border-bottom-color:#e8aaa4}.editor-actions{display:flex;gap:8px}.editor-actions button{padding:8px 10px;display:inline-flex;align-items:center;gap:6px;border-radius:6px;border:1px solid #ffffff28;color:#fff;background:#ffffff0d;font-size:12px;white-space:nowrap}.editor-actions .compile{background:#f3b943;border-color:#f3b943;color:#193023;font-weight:700}.editor-actions button.active{background:#d9eadf;color:#164c33}.layout-menu{position:relative}.editor-actions .layout-trigger{min-width:138px;justify-content:flex-start;padding-block:5px}.layout-trigger>span{min-width:0;display:flex;flex:1;flex-direction:column;align-items:flex-start;line-height:1.15}.layout-trigger>span small{color:#b9d0c2;font-size:9px;font-weight:500;letter-spacing:.025em;text-transform:uppercase}.layout-trigger.active>span small{color:#52715f}.layout-popover{position:absolute;z-index:100;top:calc(100% + 8px);right:0;width:290px;padding:7px;border:1px solid #d4dcd6;border-radius:9px;background:#fff;color:#25362c;box-shadow:0 14px 38px #061c1138}.layout-popover-title{padding:6px 8px 8px;color:#768178;font-size:10px;font-weight:700;letter-spacing:.055em;text-transform:uppercase}.editor-actions .layout-option{width:100%;min-height:52px;display:grid;grid-template-columns:25px minmax(0,1fr) 18px;gap:9px;padding:8px 9px;border:0;color:#25362c;background:transparent;text-align:left}.editor-actions .layout-option:hover,.editor-actions .layout-option.selected{background:#edf5ef;color:#185d3d}.layout-option>span{min-width:0;display:flex;flex-direction:column;align-items:flex-start;gap:2px}.layout-option strong{font-size:12px}.layout-option small{color:#748078;font-size:10px;line-height:1.35;white-space:normal}.toast{position:fixed;z-index:10;top:66px;left:50%;transform:translate(-50%);background:#8f271f;color:#fff;padding:9px 16px;border-radius:7px;font-size:13px;box-shadow:0 8px 30px #0003}.toast.success{background:#1d6b46}.work-grid{min-height:0;flex:1;background:#fff}.resize-handle{position:relative;width:6px;flex:0 0 6px;display:grid;place-items:center;background:#e4e7e4;color:#98a09a;outline:none;transition:background .12s ease}.resize-handle:hover,.resize-handle[data-resize-handle-active]{background:#89ad98;color:#fff}.resize-handle svg{pointer-events:none}.sync-resize-handle{z-index:20!important;overflow:visible}.sync-direction-buttons{position:absolute;z-index:21;top:50%;left:50%;display:flex;flex-direction:row;gap:2px;padding:3px;transform:translate(-50%,-50%);border:1px solid #aebdb3;border-radius:7px;background:#fff;box-shadow:0 4px 16px #15251b38}.sync-direction-buttons button{width:27px;height:27px;display:grid;place-items:center;border:0;border-radius:4px;padding:0;background:transparent;color:#214d35;font-size:17px;font-weight:750;line-height:1}.sync-direction-buttons button:hover:not(:disabled){background:#deebe2;color:#176b44}.sync-direction-buttons button:disabled{cursor:not-allowed;opacity:.35}.sync-resize-handle .resize-grip{opacity:0}.cm-ySelectionInfo{opacity:1!important;padding:2px 5px!important;border-radius:4px 4px 4px 0;font-family:Inter,ui-sans-serif,system-ui,sans-serif!important;font-size:10px!important;transform:translateY(-2px);box-shadow:0 2px 5px #0002}.left-panel,.context-panel,.source-panel,.preview-panel{min-width:0;min-height:0;border-right:1px solid #dfe2de}.left-panel,.source-panel,.preview-panel,.context-panel{height:100%}.left-panel{display:flex;flex-direction:column;overflow:hidden}.files-panel{position:relative;min-height:180px;flex:3;overflow:auto;border-bottom:1px solid #dfe2de}.files-panel.drop-active{outline:2px solid #2c9564;outline-offset:-2px}.file-drop-overlay{position:absolute;z-index:8;inset:42px 0 0;min-height:130px;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:6px;padding:16px;background:#e8f4edee;color:#185d3c;text-align:center;pointer-events:none;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.file-drop-overlay strong{font-size:13px}.file-drop-overlay span{max-width:90%;overflow:hidden;color:#5e7668;font-size:10px;text-overflow:ellipsis;white-space:nowrap}.outline-panel{min-height:150px;flex:2;overflow:auto}.panel-title{height:42px;display:flex;align-items:center;justify-content:space-between;padding:0 12px;border-bottom:1px solid #e6e8e5;color:#647069;text-transform:uppercase;font-size:11px;font-weight:700}.panel-title>span{display:flex;align-items:center;gap:6px}.panel-title button{width:28px;height:28px;display:grid;place-items:center;border:0;border-radius:5px;background:transparent;color:#5d6961}.panel-title button:hover{background:#e8eeea;color:#176b44}.file-tools{display:flex;gap:2px}.folder-target{min-width:0;display:flex;align-items:center;gap:6px;margin:7px 8px 0;padding:6px 8px;border-radius:5px;background:#edf2ee;color:#58645c;font-size:10px}.folder-target span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-list{padding:6px 7px 10px}.file-entry{min-width:0;height:calc(1em + 18px);min-height:30px;display:flex;align-items:center;border-radius:5px;color:#4f5952}.file-entry:hover{background:#f0f3f0}.file-entry.active,.file-entry.selected{background:#e2efe7;color:#15583a;font-weight:650}.file-entry-main{min-width:0;height:100%;flex:1;overflow:hidden;display:flex;align-items:center;gap:5px;border:0;padding:0 4px;background:transparent;color:inherit;text-align:left;font-size:inherit}.file-entry-main span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-entry-main svg{flex:none;color:#28724e}.file-entry.root-document .file-entry-main>svg{color:#2d6f9f}.file-entry.compile-target{background:#e9f2ec;box-shadow:inset 2px 0 #28724e}.file-entry-main small{flex:none;padding:1px 4px;border-radius:999px;background:#d9eadf;color:#1d5b3e;font-size:8px;line-height:1.35}.folder-entry .file-entry-main svg:first-child{color:#78837b}.folder-entry .file-entry-main svg:nth-child(2){color:#d49b2d}.root-entry{padding-left:5px}.root-entry .file-entry-main svg{color:#d49b2d}.file-entry-action{width:26px;height:26px;flex:none;display:none;place-items:center;border:0;border-radius:4px;background:transparent;color:#657069}.file-entry:hover .file-entry-action,.file-entry:focus-within .file-entry-action{display:grid}.file-entry-action:hover{background:#dfe7e1;color:#176b44}.resource-download{display:inline-flex;align-items:center;gap:6px;text-decoration:none}.resource-preview-message{min-height:180px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;color:#536158;text-align:center}.resource-preview-message strong{color:#263d2e;font-size:15px}.resource-preview-message span{max-width:440px;color:#748078;font-size:12px}.resource-image-wrap{display:flex;max-height:min(620px,65vh);align-items:center;justify-content:center;overflow:auto;padding:8px;background:#e9ecea;border-radius:7px}.resource-image{display:block;max-width:100%;max-height:min(590px,62vh);object-fit:contain}.resource-pdf{width:100%;height:min(650px,68vh);border:1px solid #d9dfda;border-radius:6px;background:#69706c}.resource-text{max-height:min(650px,68vh);overflow:auto;margin:0;padding:14px;border:1px solid #d9dfda;border-radius:6px;background:#f7f9f7;color:#26352c;font:12px/1.55 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.source-panel{position:relative;overflow:hidden}.editor-centered-notice{position:absolute;z-index:8;top:50%;left:50%;max-width:calc(100% - 32px);transform:translate(-50%,-50%);pointer-events:none;border:1px solid #69a780;border-radius:8px;padding:9px 15px;background:#1d6b46ed;color:#fff;font:600 12px/1.4 inherit;text-align:center;box-shadow:0 8px 26px #071d1238}.editor-shell{height:100%;min-height:0;display:flex;flex-direction:column}.editor-shell .editor-host{height:auto;min-height:0;flex:1 1 auto}.editor-host,.cm-editor{height:100%;min-height:0}.editor-host .cm-editor{font-family:inherit;font-size:inherit;line-height:inherit}.editor-host .cm-editor{font-variant-ligatures:contextual;font-feature-settings:"liga" 1,"calt" 1}.editor-host .cm-scroller{min-height:0;height:auto;flex:1 1 auto;overflow:auto;font-family:inherit;line-height:inherit}.editor-host .cm-panels{flex:none}.editor-host .cm-vim-panel{min-height:25px;display:flex;align-items:center;box-sizing:border-box;width:100%;border-top:1px solid #cbd8ce;padding:3px 10px;background:#f1f6f2;color:#31523e;font:11px/1.3 SFMono-Regular,Consolas,monospace}.vim-editor-status{min-height:25px;flex:0 0 25px;display:flex;align-items:center;box-sizing:border-box;border-top:1px solid #cbd8ce;padding:3px 10px;background:#f1f6f2;color:#31523e;font:11px/1.3 SFMono-Regular,Consolas,monospace}.editor-host .cm-content,.editor-host .cm-gutters{font-family:inherit;font-size:inherit}.cm-editor.cm-focused{outline:none}.editor-host .cm-spell-error-active{border-radius:2px;background:#f6d66f;box-shadow:0 0 0 1px #c99324;color:#3f3010;text-decoration:underline wavy #9b6715 1.5px;text-decoration-skip-ink:none}.editor-host .cm-panel.cm-search{padding:8px 38px 8px 10px;border-bottom:1px solid #ccd5cf;background:#f7f9f7;color:#35443a;box-shadow:0 3px 10px #1f382710;font-family:Inter,ui-sans-serif,system-ui,sans-serif;font-size:12px;line-height:1.4}.editor-host .cm-panel.cm-search .cm-textfield{min-width:170px;height:29px;margin:2px 6px 2px 0;border:1px solid #bcc8c0;border-radius:5px;padding:4px 8px;background:#fff;color:#202b24;outline:none;font-family:inherit;font-size:12px}.editor-host .cm-panel.cm-search .cm-textfield:focus{border-color:#3d8a60;box-shadow:0 0 0 2px #3d8a6025}.editor-host .cm-panel.cm-search .cm-button{height:29px;margin:2px 5px 2px 0;border:1px solid #bdc8c0;border-radius:5px;padding:3px 9px;background:#fff;background-image:none;color:#31513d;font-family:inherit;font-size:12px;text-transform:none}.editor-host .cm-panel.cm-search .cm-button:hover{border-color:#7c9f89;background:#e9f1ec}.editor-host .cm-panel.cm-search label{display:inline-flex;align-items:center;gap:3px;margin:3px 9px 3px 0;color:#59665d;font-family:inherit;font-size:12px}.editor-host .cm-panel.cm-search input[type=checkbox]{accent-color:#176b44}.editor-host .cm-panel.cm-search [name=close]{top:8px;right:10px;width:25px;height:25px;border-radius:5px;color:#647069;font-size:19px;line-height:1}.editor-host .cm-panel.cm-search [name=close]:hover{background:#e3e9e5;color:#244d36}.editor-host .cm-search-count{display:inline-flex;align-items:center;min-height:29px;margin:2px 5px 2px 4px;color:#506057;font-size:12px;font-variant-numeric:tabular-nums;white-space:nowrap}.editor-host .cm-searchMatch{background:#f5d76e99;outline:1px solid #d9a91c88}.editor-host .cm-searchMatch.cm-searchMatch-selected{background:#f0a94a99;outline-color:#cc7720}.editor-host .cm-spell-error{text-decoration:underline wavy #c53b32 1.5px;text-decoration-skip-ink:none;text-decoration-thickness:1.5px}.editor-host .cm-grammar-warning{text-decoration:underline wavy #d19a26 1.5px;text-decoration-skip-ink:none;text-decoration-thickness:1.5px}.spell-suggestions-menu{position:fixed;z-index:1200;display:flex;min-width:190px;max-width:250px;flex-direction:column;gap:3px;padding:7px;border:1px solid #b9c8bd;border-radius:7px;background:#fff;box-shadow:0 10px 28px #1930222e;color:#26352c;font-size:12px}.spell-suggestions-title{padding:3px 6px 5px;color:#607067;font-size:10px;font-weight:700;letter-spacing:.02em;text-transform:uppercase}.spell-suggestions-message{padding:3px 6px 6px;border-bottom:1px solid #e2e8e3;color:#58685e;font-size:11px;line-height:1.4}.spell-suggestions-menu button{min-height:29px;border:1px solid transparent;border-radius:5px;padding:4px 8px;background:transparent;color:#214f37;font:inherit;text-align:left;cursor:pointer}.spell-suggestions-menu button:hover:not(:disabled),.spell-suggestions-menu button:focus-visible:not(:disabled){border-color:#a9c9b3;background:#edf6ef;outline:none}.spell-suggestions-menu button:disabled{color:#78847d;cursor:not-allowed;opacity:.7}.spell-suggestions-empty,.spell-suggestions-readonly{display:block;padding:4px 6px;color:#78847d;font-size:10px;line-height:1.35}.spell-suggestions-readonly{border-top:1px solid #e2e8e3;margin-top:3px;padding-top:7px}.cm-source-comment{background:#ffe49b99;border-bottom:2px solid #d68b00;cursor:pointer}.cm-source-comment-resolved{background:#dce5de99;border-bottom-color:#829087}.cm-comment-pin{color:#d68b00;font-size:9px;padding:0 2px}.preview-panel{display:flex;flex-direction:column;background:#6f7471}.preview-tabs{min-height:48px;flex:none;display:flex;align-items:stretch;padding:5px 6px 0;background:#4d544f}.preview-tab-list{min-width:0;flex:1;display:grid;grid-template-columns:minmax(0,1.8fr) minmax(120px,1fr);gap:3px}.preview-tabs button{min-width:0;display:flex;align-items:center;justify-content:center;gap:5px;border:0;border-radius:6px 6px 0 0;background:transparent;color:#d7dcd8;font-size:11px}.preview-tabs button.active{background:#f3f4f1;color:#184f36;font-weight:700}.preview-tabs button span{min-width:17px;padding:1px 4px;border-radius:999px;background:#ffffff20;font-size:9px}.preview-tabs button.active span{background:#dfe8e2}.preview-tabs .pdf-tab{justify-content:flex-start;padding:0 12px;font-size:12px}.preview-tabs button .pdf-tab-label{min-width:0;overflow:hidden;padding:0;border-radius:0;background:transparent;display:flex;flex-direction:column;align-items:flex-start;gap:2px;font-size:12px;line-height:1;text-overflow:ellipsis;white-space:nowrap}.preview-tabs button.active .pdf-tab-label{background:transparent}.preview-tabs .pdf-tab small{max-width:100%;overflow:hidden;color:#bcc4bf;font-size:8px;font-weight:400;line-height:1;text-overflow:ellipsis;white-space:nowrap}.preview-tabs .pdf-tab.active small{color:#65756c}.pdf-download-top{flex:none;display:inline-flex;align-items:center;gap:5px;margin-left:4px;padding:0 11px;border:0;border-radius:6px 6px 0 0;color:#d7dcd8;font-size:10px;text-decoration:none;white-space:nowrap}.pdf-download-top:hover{background:#ffffff1b;color:#fff}.preview-subtabs{height:36px;flex:none;display:flex;align-items:center;gap:3px;padding:4px 7px;border-bottom:1px solid #cfd6d1;background:#e4e9e5}.preview-subtabs button{display:inline-flex;align-items:center;gap:5px;height:28px;padding:0 10px;border:0;border-radius:5px;background:transparent;color:#58665d;font-size:10px}.preview-subtabs button:hover{background:#d8e3db;color:#205c40}.preview-subtabs button.active{background:#fff;color:#184f36;font-weight:700;box-shadow:0 1px 3px #26312b22}.preview-subtabs button span{min-width:16px;padding:1px 4px;border-radius:999px;background:#d5ded7;font-size:9px;text-align:center}.preview-subtabs button.active span{background:#dfe8e2}.preview-content{min-height:0;flex:1}.preview-pdf{overflow:hidden}.pdf-viewer{position:relative;width:100%;height:100%;min-height:0;display:flex;flex-direction:column}.pdf-toolbar{height:36px;flex:none;display:flex;align-items:center;justify-content:flex-end;gap:3px;padding:4px 8px;border-bottom:1px solid #343a36;background:#414843;color:#e4e8e5;font:11px/1 Inter,ui-sans-serif,system-ui,sans-serif}.pdf-toolbar button{height:27px;min-width:27px;display:inline-flex;align-items:center;justify-content:center;gap:5px;border:1px solid #ffffff24;border-radius:5px;padding:0 6px;background:#ffffff0b;color:inherit}.pdf-toolbar button:hover:not(:disabled){border-color:#ffffff48;background:#ffffff1b}.pdf-toolbar button:disabled{opacity:.3}.pdf-zoom-value{min-width:42px;text-align:center;font-variant-numeric:tabular-nums}.pdf-toolbar .pdf-fit-width{margin-left:3px;padding:0 8px}.pdf-compiling-overlay{position:absolute;z-index:4;inset:36px 0 0;display:flex;align-items:flex-start;justify-content:center;gap:8px;padding-top:24px;background:#26312bb8;color:#fff;font-size:13px;pointer-events:none}.pdf-compiling-overlay span{padding:3px 8px;border-radius:5px;background:#17231cdd}.pdf-document{position:relative;width:100%;min-width:0;min-height:0;flex:1;overflow-x:scroll;overflow-y:auto;overscroll-behavior:contain;scrollbar-color:#aeb8b1 #4f5652;scrollbar-width:auto;scrollbar-gutter:stable;padding:14px 14px 20px;background:#6f7471}.pdf-document::-webkit-scrollbar{width:12px;height:12px}.pdf-document::-webkit-scrollbar-track{background:#4f5652}.pdf-document::-webkit-scrollbar-thumb{min-width:38px;border:2px solid #4f5652;border-radius:999px;background:#aeb8b1}.pdf-document::-webkit-scrollbar-thumb:hover{background:#d5dcd7}.pdf-document::-webkit-scrollbar-corner{background:#4f5652}.pdf-pages{min-width:100%}.pdf-page{position:relative;flex:none;max-width:none;overflow:hidden;margin:0 auto 16px;background:#fff;box-shadow:0 3px 14px #0004}.pdf-page.loading{background:#f7f8f7}.pdf-page canvas{display:block;width:100%!important;max-width:100%;height:auto!important}.pdf-page figcaption{position:absolute;right:7px;bottom:5px;padding:2px 5px;border-radius:4px;background:#26312bb5;color:#fff;font-size:9px;pointer-events:none}.pdf-sync-marker{position:absolute;z-index:2;width:14px;height:14px;border:3px solid #ef9c24;border-radius:50%;transform:translate(-50%,-50%);background:#ffe0a580;box-shadow:0 0 0 4px #ef9c2435;pointer-events:none;animation:sync-pulse 1.2s ease-out}@keyframes sync-pulse{0%{box-shadow:0 0 0 14px #ef9c2460}to{box-shadow:0 0 0 4px #ef9c2435}}.preview-empty{height:100%;display:grid;place-content:center;gap:8px;color:#e7e9e7;text-align:center}.preview-empty span{font-size:12px;color:#c2c7c4}.preview-log,.preview-warnings,.preview-errors{overflow:auto;background:#202522}.preview-clean{overflow:auto;background:#f4f6f4}.preview-artifacts{overflow:hidden;background:#f4f6f4}.artifact-browser{width:100%;height:100%;min-height:0;display:grid;grid-template-columns:minmax(135px,38%) minmax(0,1fr)}.artifact-list{min-width:0;overflow:auto;border-right:1px solid #d9dedb;background:#fff}.artifact-row{min-width:0;display:flex;align-items:stretch;border-bottom:1px solid #edf0ee}.artifact-row.active{background:#e5f0e9}.artifact-row>button{min-width:0;flex:1;display:flex;align-items:center;gap:7px;padding:8px 6px 8px 9px;border:0;background:transparent;color:#33463b;text-align:left}.artifact-row>button:disabled{cursor:default;opacity:.7}.artifact-row>button>span{min-width:0;display:flex;flex-direction:column;gap:2px}.artifact-row strong{overflow:hidden;font-size:11px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.artifact-row small{color:#89928c;font-size:9px}.artifact-row>a{width:30px;flex:none;display:grid;place-items:center;color:#52705f}.artifact-row>a:hover{background:#dce8e0;color:#176641}.artifact-preview{min-width:0;min-height:0;display:flex;flex-direction:column;background:#202522;color:#dbe2dd}.artifact-preview>header{height:36px;flex:none;display:flex;align-items:center;justify-content:space-between;gap:8px;padding:0 10px;border-bottom:1px solid #39413c;background:#303732;font-size:11px}.artifact-preview>header strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.artifact-preview>header a{flex:none;display:inline-flex;align-items:center;gap:4px;color:#c9ddd1}.artifact-preview>pre{min-height:0;flex:1;margin:0;overflow:auto;padding:13px;white-space:pre-wrap;overflow-wrap:anywhere;font:11px/1.55 SFMono-Regular,Consolas,monospace}.compile-output{min-height:100%;margin:0;padding:16px;color:#dbe2dd;white-space:pre-wrap;overflow-wrap:anywhere;font:11px/1.55 SFMono-Regular,Consolas,monospace}.compile-output.compile-warning{color:#ffdfa0;background:#2d281c}.compile-output.compile-error{color:#ffc2bd;background:#301e1d}.compile-empty{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:9px;color:#bfc7c1;font-size:12px}.compile-empty.compile-warning{color:#e8c46d;background:#2d281c}.compile-empty.compile-error{color:#e99b94;background:#301e1d}.compile-clean-panel{max-width:820px;margin:0 auto;padding:24px;color:#30453a}.compile-clean-intro{display:flex;align-items:flex-start;gap:12px;padding:15px;border:1px solid #e3d1a7;border-radius:9px;background:#fffaf0;color:#80631f;box-shadow:0 3px 12px #5c48100d}.compile-clean-intro>div{min-width:0}.compile-clean-intro strong{display:block;font-size:14px}.compile-clean-intro p,.compile-clean-actions p{margin:5px 0 0;color:#718078;font-size:11px;line-height:1.5}.compile-clean-actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-top:14px}.compile-clean-actions article{display:grid;grid-template-columns:34px minmax(0,1fr);gap:10px;min-width:0;padding:14px;border:1px solid #d4dfd7;border-radius:8px;background:#fff}.compile-clean-actions article.danger{border-color:#e3c9c5}.compile-clean-action-icon{display:grid;place-items:center;width:32px;height:32px;border-radius:7px;background:#e7f2ea;color:#28714a}.compile-clean-actions article.danger .compile-clean-action-icon{background:#f8e8e6;color:#a6433a}.compile-clean-actions strong{color:#2d4638;font-size:12px}.compile-clean-actions article.danger strong{color:#874039}.compile-clean-actions button{display:inline-flex;align-items:center;gap:5px;margin-top:12px;padding:6px 10px;border:1px solid #b8d1bd;border-radius:5px;background:#f0f8f1;color:#21633d;font-size:11px}.compile-clean-actions button:hover:not(:disabled){border-color:#79aa86;background:#e3f2e6}.compile-clean-actions button.danger{border-color:#e2b9b4;background:#fff4f2;color:#a23d35}.compile-clean-actions button.danger:hover:not(:disabled){border-color:#c8837a;background:#fce7e4}.compile-clean-actions button:disabled{cursor:not-allowed;opacity:.45}.compile-diagnostic-list{min-height:100%;padding:8px;background:#202522;overflow:auto}.compile-diagnostic-list.compile-warning{background:#2d281c}.compile-diagnostic-list.compile-error{background:#301e1d}.compile-diagnostic-row{display:grid;grid-template-columns:20px minmax(0,1fr);gap:7px;width:100%;padding:9px 10px;border:0;border-bottom:1px solid #ffffff12;border-radius:4px;background:transparent;color:#eef2ef;text-align:left}.compile-diagnostic-row:not(:disabled):hover{background:#ffffff10;box-shadow:inset 2px 0 #63b889}.compile-diagnostic-row:disabled{opacity:.78;cursor:default}.compile-diagnostic-row>span{min-width:0;display:grid;gap:4px}.compile-diagnostic-row strong{font-size:11px;font-weight:500;white-space:normal}.compile-diagnostic-row small{color:#aeb8b1;font:10px/1.4 SFMono-Regular,Consolas,monospace;overflow-wrap:anywhere}.compile-warning .compile-diagnostic-row>svg{color:#e8c46d}.compile-error .compile-diagnostic-row>svg{color:#e99b94}.context-panel{border-right:0;overflow:auto}.drawer-title{position:sticky;top:0;z-index:2;height:42px;display:flex;align-items:center;justify-content:space-between;padding:0 12px;border-bottom:1px solid #e3e6e3;background:#fff;font-size:13px}.drawer-title button{width:28px;height:28px;display:grid;place-items:center;border:0;border-radius:5px;background:transparent;color:#737a75}.drawer-title button:hover{background:#edf1ee}.tabs{height:42px;display:flex;border-bottom:1px solid #e3e6e3}.tabs button{flex:1;border:0;background:#fff;color:#707872;font-size:12px}.tabs button.active{color:#176b44;border-bottom:2px solid #176b44;font-weight:700}.outline{padding:10px 8px}.outline-item{width:100%;min-width:0;display:flex;align-items:stretch;border:0;padding:6px 0;background:transparent;font-size:12px;text-align:left}.outline-item:hover,.outline-item:focus-visible{background:#edf3ef;color:#176b44;outline:none}.outline-item.current{background:#e2efe7;color:#176b44;font-weight:700}.outline-guides{flex:none;background:repeating-linear-gradient(to right,#dfe5e0 0,#dfe5e0 1px,transparent 1px,transparent 12px)}.outline small{flex:none;min-width:22px;margin-left:8px;color:#a1a7a2}.outline-title{min-width:0;overflow:hidden;color:#3f4942;text-overflow:ellipsis;white-space:nowrap}.padded{padding:14px}.comments{padding:9px}.comments article{margin-bottom:9px;padding:10px;border:1px solid #dde2de;border-radius:7px;font-size:12px;cursor:pointer}.comments article.resolved{opacity:.55;background:#f3f5f3}.comments article.orphaned{border-color:#d8847d;background:#fff4f2}.comments header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}.comments header small{color:#858b87}.comments time{flex:none;color:#8b928d;font-size:9px;white-space:nowrap}.comment-times{flex:none;display:flex;align-items:flex-end;flex-direction:column;gap:2px}.comment-times small{font-size:8px;white-space:nowrap}.comment-author{min-width:0;display:flex;flex-direction:column;gap:1px}.comment-author strong,.comment-author small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.comment-location{margin-top:5px;color:#768079;font-size:10px}.comments blockquote{margin:8px 0;padding:6px 8px;border-left:2px solid #85ad96;background:#f2f6f3;color:#657069;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.comments p{white-space:pre-wrap}.comments .resolve{display:inline-flex;align-items:center;gap:4px;border:0;background:transparent;color:#176b44;padding:0;font-size:11px}.comment-replies{display:flex;flex-direction:column;gap:7px;margin:10px 0 8px;padding-left:9px;border-left:2px solid #d6dfd9}.comment-reply{position:relative;padding:7px 8px;border-radius:6px;background:#f5f7f5}.comment-reply p{margin:7px 0 1px}.comment-actions{display:flex;align-items:center;gap:12px}.comments .reply-action{display:inline-flex;align-items:center;gap:4px;border:0;padding:0;background:transparent;color:#526c5d;font-size:11px}.comment-owner-actions{display:inline-flex;align-items:center;gap:2px;margin-left:auto}.comment-reply>.comment-owner-actions{position:absolute;right:5px;bottom:4px}.comments .comment-owner-actions button{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:0;border-radius:5px;background:transparent;color:#66716a}.comments .comment-owner-actions button:hover{background:#e2e8e3;color:#244c35}.comments .comment-owner-actions button.danger-text:hover{background:#f8e4e2;color:#a7312a}.comment-reply-form{display:flex;flex-direction:column;gap:7px;margin-top:9px}.comment-edit-form{margin:7px 0}.comment-reply-form textarea{width:100%;resize:vertical;border:1px solid #cbd2cc;border-radius:6px;padding:7px 8px;outline:none;font-size:11px}.comment-reply-form textarea:focus{border-color:#176b44;box-shadow:0 0 0 2px #176b4420}.comment-reply-form>div{display:flex;justify-content:flex-end;gap:5px}.comment-reply-form button{display:inline-flex;align-items:center;gap:4px;border:1px solid #cbd2cc;border-radius:5px;padding:5px 7px;background:#fff;font-size:10px}.comment-reply-form button.primary{border-color:#176b44;background:#176b44;color:#fff}.settings{display:flex;flex-direction:column;gap:14px}.settings>section{display:flex;flex-direction:column;gap:14px}.settings button{border:1px solid #c9d0ca;background:#fff;border-radius:6px;padding:7px 9px;font-size:12px}.settings-tabs{display:flex;gap:4px;padding:3px;border:1px solid #d7ded8;border-radius:9px;background:#f4f7f4}.settings-tab{display:inline-flex;flex:1;align-items:center;justify-content:center;gap:6px;min-height:34px;border:1px solid transparent!important;border-radius:7px!important;background:transparent!important;color:#66716a;font-weight:650}.settings-tab:hover{background:#eaf1eb!important;color:#31523e}.settings-tab.active{border-color:#c8d9cb!important;background:#fff!important;color:#176b44;box-shadow:0 1px 2px #233c2a12}.settings-tab:focus-visible{outline:2px solid #176b44;outline-offset:1px}.settings-actions{display:flex;width:100%;justify-content:flex-end;padding-top:2px}.settings-section-title{display:flex;align-items:center;gap:7px;color:#31523e;font-size:13px}.settings-section-title.project-configuration{margin-top:4px;padding-top:15px;border-top:1px solid #e1e5e2}.settings-description{margin:0;color:#7b837e;font-size:11px;line-height:1.45}.settings .editor-checkbox{flex-direction:row;align-items:center;gap:7px;padding:8px 9px;border:1px solid #d9ded9;border-radius:7px;font-weight:500}.settings .editor-checkbox input{width:auto;margin:0;accent-color:#176b44}.spell-check-result{display:inline-flex;align-items:center;gap:6px;color:#66716a;font-size:11px}.spell-check-result.has-issues{color:#a3342c;font-weight:650}.spell-check-controls{display:inline-flex;align-items:center;gap:2px;margin-left:3px;color:#6f4a45;font-weight:500}.spell-check-controls button{width:24px;height:24px;display:inline-grid;place-items:center;border:1px solid #d7b8b4;border-radius:4px;padding:0;background:#fff8f7;color:#8e4a42}.spell-check-controls button:hover:not(:disabled){border-color:#b98279;background:#fce9e6}.spell-check-controls button:disabled{cursor:not-allowed;opacity:.35}.spell-check-position{min-width:34px;text-align:center;color:#825850;font-variant-numeric:tabular-nums;white-space:nowrap}.password-generator{display:flex;align-items:stretch;gap:7px}.password-generator input{min-width:0;flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.password-generator button{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:5px;padding:0 10px;border:1px solid #c9d0ca;border-radius:6px;background:#f6f8f6;color:#31523e;font-size:11px;white-space:nowrap}.password-generator button:hover{border-color:#8eaa98;background:#edf3ef}.settings-save{width:100%;display:inline-flex;align-items:center;justify-content:center;gap:6px}.latexmkrc-editor{resize:vertical;min-height:160px;font:12px/1.5 SFMono-Regular,Consolas,monospace!important}.dictionary-add{display:flex;align-items:stretch;gap:7px}.dictionary-add input{min-width:0;flex:1}.dictionary-add button{flex:none;color:#176b44}.dictionary-words{display:flex;flex-wrap:wrap;gap:6px}.dictionary-word{display:inline-flex;align-items:center;gap:3px;min-height:27px;padding:3px 5px 3px 8px;border:1px solid #d5ded7;border-radius:999px;background:#f1f6f2;color:#31523e;font-size:11px}.dictionary-word code{font:11px/1.2 SFMono-Regular,Consolas,monospace}.dictionary-word button{width:20px;height:20px;display:grid;place-items:center;padding:0;border:0;background:transparent;color:#748078}.dictionary-word button:hover{color:#a7312a}.dictionary-empty{color:#7b837e;font-size:11px}.dictionary-error{margin:-6px 0 0;font-size:11px}.share-dialog{display:flex;flex-direction:column;gap:16px}.share-owner{display:flex;align-items:center;gap:10px;padding:11px 13px;border-radius:8px;background:#edf4ef;color:#18583a}.share-owner>span{display:flex;flex-direction:column;gap:2px}.share-owner small{color:#6f7d74;font-size:10px;text-transform:uppercase}.share-add{display:grid;grid-template-columns:minmax(0,1fr) 150px auto;align-items:end;gap:9px;padding-bottom:16px;border-bottom:1px solid #e4e7e4}.share-add .icon-button{height:39px;display:inline-flex;align-items:center;gap:6px;padding:0 13px;font-size:12px}.shared-members-heading{display:flex;align-items:center;justify-content:space-between;color:#48524b;font-size:13px}.shared-members-heading span{min-width:22px;padding:2px 6px;border-radius:999px;background:#e8ece9;color:#6a746d;font-size:10px;text-align:center}.shared-members{display:flex;flex-direction:column;gap:7px}.shared-member{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:10px;border:1px solid #dfe3df;border-radius:8px}.member-identity{min-width:0;display:flex;align-items:center;gap:9px}.member-identity>span:last-child{min-width:0;display:flex;flex-direction:column}.member-identity strong,.member-identity small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.member-identity small{color:#7c857e;font-size:10px}.member-avatar{width:30px;height:30px;flex:none;display:grid;place-items:center;border-radius:50%;background:#dfece4;color:#176b44;font-size:12px;font-weight:750}.member-controls{display:flex;align-items:center;gap:5px}.member-controls select{border:1px solid #cbd2cc;border-radius:6px;padding:6px 8px;background:#fff;font-size:11px}.icon-only{width:31px;height:31px;display:grid;place-items:center;border:0;border-radius:6px;background:transparent}.icon-only:hover{background:#f8e9e7}.share-empty{min-height:90px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:7px;color:#89928c;font-size:12px}.spin{animation:spin .8s linear infinite}.dialog-overlay{position:fixed;z-index:100;inset:0;background:#101b15aa;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:fade-in .12s ease-out}.dialog-content{position:fixed;z-index:101;top:50%;left:50%;width:min(460px,calc(100vw - 32px));max-height:min(760px,calc(100vh - 40px));overflow:auto;transform:translate(-50%,-50%);background:#fff;border-radius:12px;box-shadow:0 25px 80px #0004;animation:dialog-in .15s ease-out}.dialog-wide{width:min(660px,calc(100vw - 32px))}.dialog-extra-wide{width:min(900px,calc(100vw - 32px))}.dialog-header{display:flex;align-items:flex-start;justify-content:space-between;padding:20px 22px 14px;border-bottom:1px solid #e5e8e5}.dialog-header h2{margin:0;font-size:19px}.dialog-header p{margin:6px 0 0;color:#737a75;font-size:12px;line-height:1.5}.dialog-close{border:0;background:transparent;color:#6d746f;font-size:22px}.dialog-body{padding:20px 22px}.dialog-body .warning{display:flex;align-items:flex-start;gap:8px;margin:0;padding:10px 11px;border:1px solid #e4cf91;border-radius:7px;background:#fff8df;color:#735a18;font-size:11px;line-height:1.5}.dialog-body .warning svg{flex:none;margin-top:1px}.system-metrics{display:grid;gap:20px}.metric-summary-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.metric-card{min-width:0;padding:14px 16px;border:1px solid #dfe4df;border-radius:10px;background:#f7f8f6;display:grid;gap:4px}.metric-card>span,.metric-card small{color:var(--muted);font-size:12px}.metric-card strong{font-size:20px;font-weight:650}.metric-section{display:grid;gap:10px}.metric-section h3{margin:0;display:flex;align-items:center;gap:7px;font-size:14px}.metric-section p{margin:0;font-size:13px}.metric-table-wrap{overflow-x:auto;border:1px solid #dfe4df;border-radius:9px}.metric-table{width:100%;border-collapse:collapse;font-size:12px;white-space:nowrap}.metric-table th,.metric-table td{padding:8px 10px;border-bottom:1px solid #e4e8e4;text-align:right}.metric-table th:first-child,.metric-table td:first-child{text-align:left}.metric-table tbody tr:last-child td{border-bottom:0}@media(max-width:640px){.metric-summary-grid{grid-template-columns:1fr}}.dialog-footer{display:flex;justify-content:flex-end;gap:8px;padding:14px 22px;border-top:1px solid #e5e8e5;background:#f8f9f7}.dialog-footer button{border:1px solid #cbd2cc;border-radius:7px;padding:8px 14px;background:#fff}.dialog-footer .primary{display:inline-flex;align-items:center;justify-content:center;gap:6px;border-color:#176b44;background:#176b44;color:#fff}.dialog-footer .danger,button.danger{border-color:#b3261e;background:#b3261e;color:#fff}.form-stack{display:flex;flex-direction:column;gap:14px}.import-error{margin:0;padding:10px 12px;border:1px solid #e3b5b1;border-radius:8px;background:#fff3f2;line-height:1.45}.form-field{display:flex;flex-direction:column;gap:7px;font-size:13px;font-weight:650}.form-field input,.form-field select,.form-field textarea{width:100%;border:1px solid #cbd2cc;border-radius:8px;padding:10px 11px;background:#fff;outline:none}.form-field input:focus,.form-field select:focus,.form-field textarea:focus{border-color:#176b44;box-shadow:0 0 0 3px #176b4420}.upload-picker{position:relative;min-height:116px;display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:14px;padding:20px;border:1.5px dashed #aebbb2;border-radius:10px;background:#f6f8f6;color:#5f6b63;transition:border-color .15s ease,background .15s ease}.upload-picker:hover,.upload-picker:focus-within{border-color:#4f8a68;background:#f0f6f2}.upload-picker.has-file{border-style:solid;border-color:#83aa91;background:#edf5ef;color:#276044}.upload-picker>svg{flex:none;color:#176b44}.upload-picker-copy{min-width:0;display:flex;flex-direction:column;gap:4px}.upload-picker-copy strong,.upload-picker-copy span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.upload-picker-copy strong{color:#29382f;font-size:13px}.upload-picker-copy span{color:#758078;font-size:11px}.upload-picker>button{display:inline-flex;align-items:center;gap:6px;border:1px solid #9fb0a4;border-radius:7px;padding:8px 11px;background:#fff;color:#235c3d;font-size:12px;font-weight:650}.upload-picker>button:hover{border-color:#176b44;background:#e4efe8}.upload-picker>.upload-clear{position:absolute;top:7px;right:7px;width:26px;height:26px;display:grid;place-items:center;border:0;padding:0;background:transparent;color:#6c776f}.upload-picker>.upload-clear:hover{background:#dce9e0;color:#9d2f27}.choice-group{display:flex;flex-direction:column;gap:10px;border:1px solid #dfe3df;border-radius:8px;padding:14px}.choice-group legend{padding:0 5px;font-size:12px;color:#69716b}.choice-group label{font-size:13px}.color-picker{display:grid;grid-template-columns:repeat(2,1fr);gap:7px;border:0;padding:0}.color-picker legend{margin-bottom:8px;font-size:13px;font-weight:650}.color-picker label{display:flex;align-items:center;gap:7px;border:1px solid #dce1dd;border-radius:7px;padding:8px;font-size:12px}.color-picker label.active{border-color:#176b44;background:#edf5f0}.color-picker input{position:absolute;opacity:0;pointer-events:none}.tag-assignment-list{display:flex;flex-direction:column;gap:7px}.tag-assignment-list label,.tag-assignment-list button{display:flex;align-items:center;gap:9px;min-height:38px;border:1px solid #dce1dd;border-radius:7px;padding:8px 10px;background:#fff;color:#38423b;font-size:13px;text-align:left}.tag-assignment-list label:hover,.tag-assignment-list button:hover{border-color:#8bae9a;background:#f4f8f5}.tag-assignment-list input{width:auto;accent-color:#176b44}.checkbox-field{display:flex;align-items:center;gap:8px;font-size:13px}.checkbox-field input{width:auto}.selection-preview{max-height:130px;overflow:auto;margin:14px 0 0;padding:10px 12px;border-left:3px solid #d6a128;background:#fff8e5;white-space:pre-wrap;font:12px/1.5 SFMono-Regular,Consolas,monospace}.git-dialog{display:flex;flex-direction:column;gap:14px}.git-dialog-modal-fullscreen{top:0;left:0;width:100vw;height:100vh;max-height:100vh;transform:none;overflow:hidden;border-radius:0}.git-loading{min-height:120px;display:flex;align-items:center;justify-content:center;gap:8px;color:#6e7871;font-size:13px}.git-message{margin:0;display:flex;align-items:center;gap:7px;padding:9px 11px;border:1px solid;border-radius:7px;font-size:12px}.git-message.error{border-color:#e2b4b0;background:#fff2f1}.git-message.success{border-color:#acd0b8;background:#edf7f0;color:#17623d}.git-section{display:flex;flex-direction:column;gap:11px;padding:14px;border:1px solid #dfe4e0;border-radius:9px;background:#fbfcfb}.git-section-toggle{width:100%;display:flex;align-items:center;justify-content:space-between;gap:10px;border:0;padding:0;background:transparent;color:#607068;text-align:left}.git-section-toggle:hover{color:#176b44}.git-section-heading{min-width:0;display:flex;align-items:flex-start;gap:8px;color:#27533a}.git-section-heading>svg{flex:none;margin-top:1px}.git-section-heading>div,.git-section-heading>span{min-width:0;display:flex;flex-direction:column;gap:3px}.git-section-heading strong{font-size:13px}.git-section-heading small{max-width:650px;overflow:hidden;color:#78817b;font-size:10px;line-height:1.4;text-overflow:ellipsis;white-space:nowrap}.git-section-content{display:flex;flex-direction:column;gap:11px;padding-top:2px}.git-token-guidance{display:flex;flex-direction:column;gap:4px;padding:10px 11px;border:1px solid #c6d9cb;border-left:3px solid #28714b;border-radius:7px;background:#f0f7f2}.git-token-guidance strong{color:#225d3c;font-size:11px}.git-token-guidance span{color:#66756b;font-size:10px;line-height:1.5}.git-account,.git-repository-card{display:flex;align-items:center;gap:8px;padding:9px 10px;border-radius:7px;background:#eaf3ed;color:#23563a;font-size:12px}.git-account>span{min-width:0;flex:1}.git-account button{display:inline-flex;align-items:center;gap:4px;border:0;background:transparent;color:#a3342c;font-size:10px}.git-inline-actions,.git-action-row{display:flex;align-items:center;flex-wrap:wrap;gap:7px}.git-inline-actions{justify-content:space-between}.git-inline-actions a,.git-repository-card a{display:inline-flex;align-items:center;gap:4px;color:#176b44;font-size:11px;text-decoration:none}.git-inline-actions button,.git-action-row button,.git-primary-action{display:inline-flex;align-items:center;justify-content:center;gap:5px;border:1px solid #bdc9c0;border-radius:6px;padding:7px 9px;background:#fff;color:#31523e;font-size:11px}.git-action-row .git-discard-button:not(:disabled){border-color:#d9aaa5;color:#9b332b}.git-action-row .git-discard-button:not(:disabled):hover{background:#fff0ee}.git-primary-action{align-self:flex-start;border-color:#176b44;background:#176b44;color:#fff}.git-repository-card{justify-content:space-between}.git-repository-card>div{min-width:0;display:flex;flex-direction:column;gap:3px}.git-repository-card strong,.git-repository-card small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.git-repository-card small{color:#6e7c73;font-size:10px}.git-count{min-width:17px;padding:1px 5px;border-radius:999px;background:#d9e9df;font-size:9px}.git-diff-section{padding-bottom:0;overflow:hidden}.git-diff-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}.git-diff-controls{display:flex;align-items:center;gap:7px;flex:none}.git-diff-font-controls{display:inline-flex;align-items:center;gap:2px;border:1px solid #cbd5ce;border-radius:6px;padding:2px;background:#fff;color:#52685a}.git-diff-font-button{width:25px;height:25px;display:grid;place-items:center;border:0;border-radius:4px;padding:0;background:transparent;color:inherit}.git-diff-font-button:hover:not(:disabled){background:#edf4ef;color:#176b44}.git-diff-font-value{min-width:34px;color:#52685a;font-size:10px;font-variant-numeric:tabular-nums;text-align:center}.git-diff-fullscreen{width:29px;height:29px;flex:none;display:grid;place-items:center;padding:0!important;border:1px solid #cbd5ce!important;border-radius:6px!important;background:#fff!important;color:#52685a}.git-diff-fullscreen:hover{border-color:#8eaa98!important;background:#edf4ef!important;color:#176b44}.git-diff-section.is-fullscreen,.git-diff-section:fullscreen{position:fixed;z-index:1000;inset:0;width:100vw;height:100vh;max-height:none;box-sizing:border-box;margin:0;padding:20px!important;border:0;border-radius:0;overflow:hidden;background:#fbfcfb}.git-diff-section.is-fullscreen .git-diff,.git-diff-section:fullscreen .git-diff{flex:1;min-height:0;max-height:none;margin:0}.git-diff-section.is-fullscreen .git-diff-empty,.git-diff-section:fullscreen .git-diff-empty{flex:1;min-height:0;margin:0}.git-diff{max-height:360px;margin:0 -14px;overflow:auto;padding:11px 0;background:#202522;color:#d9e1dc;font:10px/1.5 SFMono-Regular,Consolas,monospace;tab-size:4}.git-diff span{display:block;min-width:max-content;padding:0 12px;white-space:pre}.git-diff span.addition{background:#173b28;color:#bfe8cb}.git-diff span.deletion{background:#442421;color:#f1c1bc}.git-diff span.hunk{background:#283a48;color:#b8daf4}.git-diff span.header{color:#e8cf8b;font-weight:650}.git-diff-empty{margin:0 -14px;padding:28px;background:#252a27;color:#b9c0bc;font-size:12px;text-align:center}.git-history{max-height:300px;display:flex;flex-direction:column;gap:5px;overflow:auto}.git-history article{min-width:0;display:grid;grid-template-columns:66px minmax(0,1fr) auto;align-items:center;gap:8px;padding:8px;border:1px solid #e2e6e3;border-radius:6px;background:#fff}.git-history code{color:#176b44;font-size:10px}.git-history article>div{min-width:0;display:flex;flex-direction:column;gap:2px}.git-history article strong,.git-history article small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.git-history article strong{font-size:11px}.git-history article small{color:#7b847e;font-size:9px}.git-history article>span{display:flex;gap:3px}.git-history button{width:27px;height:27px;display:grid;place-items:center;border:0;border-radius:5px;background:transparent;color:#52685a}.git-history button:hover{background:#e8f0eb;color:#176b44}.git-force-checkout{display:flex;align-items:flex-start;gap:9px;padding:11px;border:1px solid #e4b9b5;border-radius:8px;background:#fff6f5}.git-force-checkout input{margin-top:2px;accent-color:#b3261e}.git-force-checkout span{display:flex;flex-direction:column;gap:3px}.git-force-checkout strong{color:#932c25;font-size:12px}.git-force-checkout small{color:#7d6562;font-size:10px;line-height:1.45}.quick-open-dialog>label,.project-search-query{display:flex;align-items:center;gap:8px;border:1px solid #bfcac2;border-radius:7px;padding:0 10px;background:#fff}.quick-open-dialog>label:focus-within,.project-search-query:focus-within{border-color:#418060;box-shadow:0 0 0 2px #41806020}.quick-open-dialog input,.project-search-query input{width:100%;border:0;outline:0;padding:10px 0;background:transparent}.quick-open-results{max-height:420px;overflow:auto;margin-top:8px;border:1px solid #d9e0db;border-radius:7px}.quick-open-results button{display:grid;grid-template-columns:20px minmax(0,1fr) 18px;align-items:center;gap:6px;width:100%;padding:8px 10px;border:0;border-radius:0;background:#fff;text-align:left}.quick-open-results button+button{border-top:1px solid #eef1ef}.quick-open-results button.active{background:#e4efe8;color:#195f3d}.quick-open-results span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:11px/1.4 JetBrains Mono,monospace}.project-search-dialog{display:grid;gap:9px}.project-search-form{display:grid;grid-template-columns:1fr 1fr;gap:8px}.project-search-options{grid-column:1 / -1;display:flex;align-items:center;gap:14px}.project-search-options label{display:inline-flex;align-items:center;gap:5px;color:#55645a;font-size:11px}.project-search-options label input{accent-color:#28734b}.project-search-options button{margin-left:auto;display:inline-flex;align-items:center;gap:5px}.project-search-summary{display:flex;align-items:center;justify-content:space-between;min-height:22px;color:#5e6c63;font-size:11px}.project-search-summary span{display:inline-flex;align-items:center;gap:5px}.project-search-results{max-height:480px;overflow:auto;border:1px solid #d9e0db;border-radius:7px;background:#fff}.project-search-results>button{display:grid;grid-template-columns:20px minmax(0,1fr);gap:7px;width:100%;padding:9px 10px;border:0;border-radius:0;background:#fff;text-align:left}.project-search-results>button+button{border-top:1px solid #e8ece9}.project-search-results>button:hover{background:#edf4ef}.project-search-results>button>span{min-width:0;display:grid;gap:4px}.project-search-results strong{color:#31503d;font-size:11px}.project-search-results strong small{color:#7c877f;font-weight:400}.project-search-results code{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#4e5952;font:10px/1.45 JetBrains Mono,monospace}.project-search-results mark{border-radius:2px;padding:1px;background:#f2d36e;color:#4b3b0b}.history-dialog-modal{width:min(1120px,calc(100vw - 32px))}.history-dialog{display:grid;grid-template-columns:270px minmax(0,1fr);min-height:560px;max-height:min(72vh,760px);border:1px solid #d9e0db;border-radius:8px;overflow:hidden;background:#fff}.history-loading{grid-column:1 / -1;display:flex;align-items:center;justify-content:center;gap:8px;min-height:420px;color:#68736b}.history-message{grid-column:1 / -1;margin:10px}.history-timeline{overflow:auto;border-right:1px solid #d9e0db;background:#f5f7f5}.history-stats{position:sticky;top:0;z-index:2;display:grid;gap:5px;padding:10px 12px;border-bottom:1px solid #d8dfda;background:#edf3ef;color:#33483b}.history-stats header{display:flex;align-items:center;justify-content:space-between;gap:8px}.history-stats header span{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:700}.history-stats header button{width:26px;height:26px;display:grid;place-items:center;border:0;padding:0;background:transparent;color:#9d332b}.history-stats header button:hover:not(:disabled){background:#f5deda}.history-stats strong{font-size:11px}.history-stats small{color:#68766d;font-size:9px;line-height:1.4}.history-stats progress{width:100%;height:6px;border:0;border-radius:999px;overflow:hidden;accent-color:#34845a}.history-stats.exceeded progress{accent-color:#bd483e}.history-stats .history-storage-warning{color:#9d332b}.history-timeline>button{display:grid;grid-template-columns:20px minmax(0,1fr);gap:7px;width:100%;padding:11px 12px;border:0;border-bottom:1px solid #e1e6e2;border-radius:0;background:transparent;text-align:left;color:#34443a}.history-timeline>button:hover{background:#eaf0ec}.history-timeline>button.active{background:#dfece4;box-shadow:inset 3px 0 #28734b}.history-timeline>button span{min-width:0;display:grid;gap:2px}.history-timeline>button strong,.history-timeline>button small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.history-timeline>button small{color:#68736b;font-size:10px}.history-detail{min-width:0;overflow:auto;padding:14px}.history-detail-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.history-detail-header>div{display:grid;gap:4px}.history-detail-header small{display:flex;align-items:center;gap:4px;color:#68736b}.history-detail-actions{display:flex;align-items:center;gap:6px}.history-detail-actions button{display:inline-flex;align-items:center;gap:5px;white-space:nowrap}.history-label{display:flex;gap:8px;margin-bottom:12px}.history-label label{flex:1;display:flex;align-items:center;gap:7px;padding:0 9px;border:1px solid #cdd6cf;border-radius:6px;background:#fff}.history-label input{width:100%;border:0;padding:8px 0;outline:0}.history-files{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:start}.history-file-list{display:flex;gap:5px;overflow-x:auto;padding-bottom:5px}.history-file-list button{display:inline-flex;align-items:center;gap:5px;flex:0 0 auto;max-width:230px}.history-file-list button span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.history-file-list button small{padding:1px 4px;border-radius:8px;background:#f2d675;color:#695414;font-size:9px}.history-file-list button.active{border-color:#418060;background:#e7f0ea}.history-file-actions{display:flex;gap:5px}.history-file-actions button{display:inline-flex;align-items:center;gap:5px;white-space:nowrap}.history-comparison{min-height:360px;margin-top:10px;border:1px solid #d8dfda;border-radius:7px;overflow:hidden;background:#fbfcfb}.history-comparison>header{display:flex;align-items:baseline;justify-content:space-between;gap:10px;padding:8px 10px;border-bottom:1px solid #d8dfda;background:#f1f4f2}.history-comparison>header small{color:#68736b}.history-comparison pre{min-height:330px;max-height:430px;margin:0;overflow:auto;padding:11px;white-space:pre-wrap;overflow-wrap:anywhere;font:11px/1.5 JetBrains Mono,Source Code Pro,monospace;color:#26342b}.history-comparison pre .history-removed{background:#f8c9c9;color:#7b2525;text-decoration:line-through}.history-comparison pre .history-added{background:#ccebd5;color:#15552c}.history-comparison-empty{min-height:360px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;color:#78827b}@keyframes fade-in{0%{opacity:0}}@keyframes dialog-in{0%{opacity:0;transform:translate(-50%,-48%) scale(.98)}}@keyframes spin{to{transform:rotate(360deg)}}@media(max-width:1100px){.project-grid.list-view .project-card-open{grid-template-columns:110px minmax(160px,1fr)}.project-grid.list-view .project-meta{grid-column:1 / -1}.editor-actions button{padding:8px}.editor-actions button svg+*{display:none}}@media(max-width:720px){.upload-picker{grid-template-columns:auto 1fr}.upload-picker>button{grid-column:1 / -1;justify-content:center}.share-add{grid-template-columns:1fr}.shared-member{align-items:flex-start}.member-controls{flex:none}.history-dialog{grid-template-columns:1fr;max-height:none}.history-timeline{max-height:180px;border-right:0;border-bottom:1px solid #d9e0db}.history-files,.project-search-form{grid-template-columns:1fr}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{i as e}from"./index-
|
|
1
|
+
import{i as e}from"./index-BLh_AgbG.js";const i=e("Maximize2",[["polyline",{points:"15 3 21 3 21 9",key:"mznyad"}],["polyline",{points:"9 21 3 21 3 15",key:"1avn1i"}],["line",{x1:"21",x2:"14",y1:"3",y2:"10",key:"ota7mn"}],["line",{x1:"3",x2:"10",y1:"21",y2:"14",key:"1atl0r"}]]);const s=e("Minus",[["path",{d:"M5 12h14",key:"1ays0h"}]]);const y=e("Plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);export{s as M,y as P,i as a};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-BDbf4yIq.js","assets/BinaryModule-Aj1vLnwf-CqJDurFy.js","assets/index-BLh_AgbG.js","assets/react-CsO_xvbl.js","assets/index-bVUcyCfK.css","assets/binary-DhyLq3C-.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{_ as w}from"./index-BLh_AgbG.js";import"./react-CsO_xvbl.js";const k=/([A-Za-z][A-Za-z0-9_.:/-]*(?:\s+[A-Za-z][A-Za-z0-9_.:/-]*){0,7})\s*$/,S=new Set(["tabular","tabularx","tabulary","longtable","array","matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix"]),A=new Set(["math","displaymath","equation","equation*","align","align*","alignat","alignat*","gather","gather*","multline","multline*","flalign","flalign*","tikzpicture","axis","scope","pgfonlayer","pgfpicture","tabular","tabularx","tabulary","longtable","array","matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix","verbatim","Verbatim","lstlisting","minted","comment","algorithmic","alignat"]),b=new Set(["documentclass","usepackage","RequirePackage","includegraphics","tikzset","pgfplotsset","hypersetup","lstset","definecolor","colorlet","setlength","setcounter","draw","path","fill","filldraw","shade","node","addplot","color","textcolor","colorbox","pagecolor"]),L=new Set(["label","hypertarget","ref","pageref","autoref","nameref","hyperref","index","gls","Gls","glspl","Glspl","cite","citep","citet","citeauthor","citeyear","citenum","parencite","textcite","autocite","footcite"]),z=5;let d=null,_=Promise.resolve(),x="";function c(t,i,n){n>i&&t.push({from:i,to:n})}function p(t,i,n,a){if(t[i]!==n)return null;let e=0;for(let r=i;r<t.length;r+=1){const l=t[r-1]==="\\"&&t[r-2]!=="\\";if(!l&&t[r]===n&&(e+=1),!l&&t[r]===a&&(e-=1,e===0))return{from:i,to:r+1,end:r+1}}return null}function u(t,i){let n=i;for(;n<t.length&&/[ \t\r\n]/.test(t[n]);)n+=1;return n}function E(t,i){if(t[i]!=="%")return!1;let n=0;for(let a=i-1;a>=0&&t[a]==="\\";a-=1)n+=1;return n%2===0}function P(t){return/[=,!/]/.test(t)||/\b(?:colorbar|legend|width|height|draw|fill|style|domain|samples|anchor|at|axis)\b/.test(t)}function O(t){return L.has(t)||/^cite[A-Za-z]*/.test(t)}function Z(t,i){for(let n=t.indexOf("=");n>=0;n=t.indexOf("=",n+1)){const e=Math.max(...[`
|
|
3
|
+
`,"[","{","]","}",",",";"].map(o=>t.lastIndexOf(o,n-1)+1)),l=t.slice(e,n).match(k);l?.index!==void 0&&c(i,e+l.index,n);let f=n+1;for(;f<t.length&&/\s/.test(t[f]);)f+=1;let s=f;if(t[f]==="{")s=p(t,f,"{","}")?.end??f;else for(;s<t.length&&!/[,\]\}\n]/.test(t[s]);)s+=1;c(i,f,s)}}function I(t,i,n){const a=/\\(begin|end)\s*\{\s*([^{}]+?)\s*\}/g;a.lastIndex=i;let e=1;for(let r=a.exec(t);r;r=a.exec(t))if(r[2].trim()===n){if(r[1]==="begin")e+=1;else if(--e===0)return a.lastIndex}return null}function R(t,i){for(const n of t.matchAll(/\\\((?:\\.|[^])*?\\\)|\\\[(?:\\.|[^])*?\\\]/g))n.index!==void 0&&c(i,n.index,n.index+n[0].length);for(let n=0;n<t.length;n+=1){if(t[n]!=="$"||t[n-1]==="\\")continue;const a=t[n+1]==="$"?"$$":"$",e=t.indexOf(a,n+a.length);e>=0&&(c(i,n,e+a.length),n=e+a.length-1)}}function $(t){const i=[],n=/[A-Za-z@]/;for(let e=0;e<t.length;e+=1){if(E(t,e)){const o=t.indexOf(`
|
|
4
|
+
`,e);c(i,e,o<0?t.length:o),e=o<0?t.length:o;continue}if(t[e]!=="\\")continue;const r=e;let l=e+1;if(n.test(t[l]??""))for(;l<t.length&&/[A-Za-z@0-9:_]/.test(t[l]);)l+=1;else l<t.length&&(l+=1);const f=t.slice(e+1,l);c(i,r,l);let s=u(t,l);if(f==="begin"||f==="end"){const o=p(t,s,"{","}");if(o){c(i,o.from,o.to);const h=t.slice(o.from+1,o.to-1).trim();if(s=u(t,o.end),f==="begin"){const g=p(t,s,"[","]");if(g&&(c(i,g.from,g.to),s=u(t,g.end)),S.has(h)){const m=p(t,s,"{","}");m&&c(i,m.from,m.to)}if(A.has(h)){const m=I(t,o.end,h);if(m!==null){c(i,r,m),e=m-1;continue}}}}e=l-1;continue}if(O(f))for(t[s]==="*"&&(s=u(t,s+1));;){const o=p(t,s,"[","]")??p(t,s,"{","}");if(!o)break;c(i,o.from,o.to),s=u(t,o.end)}else{const o=p(t,s,"[","]");if(o&&(b.has(f)||P(t.slice(o.from+1,o.to-1)))&&(c(i,o.from,o.to),s=u(t,o.end)),b.has(f)){const h=f==="definecolor"||f==="colorlet"?3:1;for(let g=0;g<h;g+=1){const m=p(t,s,"{","}");if(!m)break;c(i,m.from,m.to),s=u(t,m.end)}}}e=l-1}R(t,i),Z(t,i);for(const e of t.matchAll(/(?:https?|ftp):\/\/[^\s]+/gi))e.index!==void 0&&c(i,e.index,e.index+e[0].length);i.sort((e,r)=>e.from-r.from||e.to-r.to);const a=[];for(const e of i){const r=a.at(-1);r&&e.from<=r.to?r.to=Math.max(r.to,e.to):a.push({...e})}return a}function T(t){const i=$(t),n=[];let a=0,e=0;for(const r of t){for(;e<i.length&&i[e].to<=a;)e+=1;const l=i[e];n.push(l&&l.from<=a&&a<l.to&&r!==`
|
|
5
|
+
`&&r!=="\r"?" ":r),a+=r.length}return n.join("")}function V(t){const i=[0];let n=0;for(const a of t)n+=a.length,i.push(n);return i}function B(t){return[...new Set(t.map(i=>i.trim()).filter(Boolean))].sort((i,n)=>i.localeCompare(n)).join("\0")}async function C(t,i){const n=_.then(async()=>{if(!d)try{const[{WorkerLinter:e,LocalLinter:r},{binary:l}]=await Promise.all([w(()=>import("./index-BDbf4yIq.js"),__vite__mapDeps([0,1,2,3,4])),w(()=>import("./binary-DhyLq3C-.js"),__vite__mapDeps([5,1,2,3,4]))]),f=typeof Worker<"u";try{d=new(f?e:r)({binary:l}),await d.setup()}catch(s){const o=d;if(d=null,await o?.dispose().catch(()=>{}),!f)throw s;d=new r({binary:l}),await d.setup()}}catch(e){throw d=null,x="",e}const a=B(t);return a!==x&&(await d.clearWords(),t.length&&await d.importWords([...new Set(t.map(e=>e.trim()).filter(Boolean))]),x=a),i(d)});return _=n.then(()=>{},()=>{}),n}function q(t){return t.get_replacement_text()}function W(t,i,n,a,e){const r={from:a.start,to:a.end},l={from:e[a.start]??-1,to:e[a.end]??-1},f=[r,l].filter((o,h,g)=>o.from>=0&&o.to>o.from&&o.to<=t.length&&g.findIndex(m=>m.from===o.from&&m.to===o.to)===h);if(f.length===0)return null;const s=n.get_problem_text();return f.find(o=>i.slice(o.from,o.to)===s)??f.find(o=>/[A-Za-z]/.test(i.slice(o.from,o.to)))??f[0]??null}function D(t,i,n,a){const e=n.span();try{const r=W(t,i,n,e,a);if(!r)return null;const{from:l,to:f}=r;if(!/[A-Za-z]/.test(i.slice(l,f)))return null;const s=t.slice(l,f);if(!s.trim()||/[\\%$]/.test(s))return null;const o=n.lint_kind()==="Spelling"||n.lint_kind()==="Typo"?"spelling":"grammar",h=n.suggestions(),g=h.map(q).filter((m,y,v)=>y<z&&v.indexOf(m)===y);for(const m of h)m.free();return{from:l,to:f,word:s,kind:o,message:n.message(),suggestions:g}}finally{e.free()}}function G(t,i=[]){const n=T(t),a=V(t);return C(i,async e=>{const r=await e.lint(n,{language:"plaintext",dedup:!0,isolateEnglish:!1});try{return r.map(l=>D(t,n,l,a)).filter(l=>!!l).sort((l,f)=>l.from-f.from||l.to-f.to)}finally{for(const l of r)l.free()}})}export{$ as ignoredRanges,G as lintLatex,T as maskLatexSource};
|
package/dist/client/index.html
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/iosevka@5.2.5/400.css" />
|
|
11
11
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
|
12
12
|
<title>TexLite</title>
|
|
13
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
+
<script type="module" crossorigin src="/assets/index-BLh_AgbG.js"></script>
|
|
14
14
|
<link rel="modulepreload" crossorigin href="/assets/react-CsO_xvbl.js">
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-bVUcyCfK.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
18
18
|
<div id="root"></div>
|
package/dist/server/compiler.js
CHANGED
|
@@ -179,6 +179,51 @@ async function copyAndDigest(source, destination) {
|
|
|
179
179
|
export function discardCompileSnapshot(snapshot) {
|
|
180
180
|
fs.rmSync(snapshot.root, { recursive: true, force: true });
|
|
181
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Returns whether the incremental latexmk cache for a root document exists.
|
|
184
|
+
* A missing cache is intentionally treated as a request for a full rebuild,
|
|
185
|
+
* even when a published PDF for the same source revision is still available.
|
|
186
|
+
*/
|
|
187
|
+
export function hasCompileCache(config, projectId, mainFile) {
|
|
188
|
+
const cacheRoot = compileCacheTargetRoot(config, projectId, mainFile);
|
|
189
|
+
try {
|
|
190
|
+
return fs.readdirSync(cacheRoot, { withFileTypes: true }).some((entry) => entry.isDirectory());
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/** Remove only incremental compiler state; the last published PDF remains available. */
|
|
197
|
+
export function cleanCompileCache(config, projectId, mainFile) {
|
|
198
|
+
fs.rmSync(compileCacheTargetRoot(config, projectId, mainFile), { recursive: true, force: true });
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Remove generated output for one root document without touching project
|
|
202
|
+
* source, settings, comments, history, or Git data.
|
|
203
|
+
*/
|
|
204
|
+
export function cleanCompileArtifacts(config, projectId, mainFile, defaultMainFile, runIds = []) {
|
|
205
|
+
const published = publishedCompileArtifacts(config, projectId, mainFile, mainFile === defaultMainFile);
|
|
206
|
+
cleanCompileCache(config, projectId, mainFile);
|
|
207
|
+
fs.rmSync(compileTargetRoot(config, projectId, mainFile), { recursive: true, force: true });
|
|
208
|
+
const runIdsToRemove = new Set(runIds);
|
|
209
|
+
if (published)
|
|
210
|
+
runIdsToRemove.add(published.runId);
|
|
211
|
+
for (const runId of runIdsToRemove) {
|
|
212
|
+
if (/^[a-f0-9-]{36}$/i.test(runId))
|
|
213
|
+
fs.rmSync(compileRunRoot(config, projectId, runId), { recursive: true, force: true });
|
|
214
|
+
}
|
|
215
|
+
if (mainFile === defaultMainFile) {
|
|
216
|
+
const output = outputRoot(config, projectId);
|
|
217
|
+
for (const generated of [
|
|
218
|
+
path.join(output, ".texlite", "latest.json"),
|
|
219
|
+
path.join(output, ".texlite", "latest.pdf"),
|
|
220
|
+
path.join(output, ".texlite", "latest.synctex.gz"),
|
|
221
|
+
path.join(output, `${path.basename(mainFile, ".tex")}.pdf`),
|
|
222
|
+
path.join(output, `${path.basename(mainFile, ".tex")}.synctex.gz`)
|
|
223
|
+
])
|
|
224
|
+
fs.rmSync(generated, { force: true });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
182
227
|
export function publishCompileArtifacts(config, projectId, snapshot, result) {
|
|
183
228
|
if (!result.ok || !result.pdfPath)
|
|
184
229
|
throw new Error("无法发布失败的编译结果");
|
|
@@ -284,6 +329,9 @@ function compileDataRoot(config, projectId) {
|
|
|
284
329
|
function compileTargetRoot(config, projectId, mainFile) {
|
|
285
330
|
return path.join(compileDataRoot(config, projectId), "targets", compileTargetKey(mainFile));
|
|
286
331
|
}
|
|
332
|
+
function compileCacheTargetRoot(config, projectId, mainFile) {
|
|
333
|
+
return path.join(compileDataRoot(config, projectId), "cache", compileTargetKey(mainFile));
|
|
334
|
+
}
|
|
287
335
|
function compileTargetKey(mainFile) {
|
|
288
336
|
return createHash("sha256").update(safeRelativePath(mainFile)).digest("hex").slice(0, 24);
|
|
289
337
|
}
|
|
@@ -305,7 +353,7 @@ function compileCacheKey(config, snapshot, mainFile, engine, latexmkrc) {
|
|
|
305
353
|
return hash.digest("hex").slice(0, 24);
|
|
306
354
|
}
|
|
307
355
|
function prepareCompileCache(config, snapshot, mainFile, engine, latexmkrc) {
|
|
308
|
-
const cacheDirectory =
|
|
356
|
+
const cacheDirectory = compileCacheTargetRoot(config, snapshot.projectId, mainFile);
|
|
309
357
|
const key = compileCacheKey(config, snapshot, mainFile, engine, latexmkrc);
|
|
310
358
|
const root = path.join(cacheDirectory, key);
|
|
311
359
|
const cacheSource = path.join(root, "source");
|
|
@@ -5,6 +5,28 @@ const definitionExtensions = /\.(?:tex|sty|cls|def|cfg|dtx|ins)$/i;
|
|
|
5
5
|
const maxIndexedFileBytes = 3 * 1024 * 1024;
|
|
6
6
|
const maxIndexedBytes = 24 * 1024 * 1024;
|
|
7
7
|
const maxIndexedFiles = 500;
|
|
8
|
+
// Core LaTeX commands are kept in the completion index even when the project
|
|
9
|
+
// has not loaded a package-specific definition yet. Keep the catalogue
|
|
10
|
+
// grouped by purpose so missing primitives are easy to spot and extend.
|
|
11
|
+
const standardNoArgumentCommands = [
|
|
12
|
+
["\\noindent", "Suppress paragraph indentation"], ["\\indent", "Insert paragraph indentation"], ["\\par", "End paragraph"],
|
|
13
|
+
["\\leavevmode", "Leave vertical mode"], ["\\newline", "Line break"], ["\\linebreak", "Suggest line break"], ["\\nolinebreak", "Discourage line break"],
|
|
14
|
+
["\\pagebreak", "Suggest page break"], ["\\nopagebreak", "Discourage page break"], ["\\newpage", "Page break"], ["\\clearpage", "Flush floats and page break"], ["\\cleardoublepage", "Flush floats to an odd page"],
|
|
15
|
+
["\\smallskip", "Small vertical space"], ["\\medskip", "Medium vertical space"], ["\\bigskip", "Large vertical space"], ["\\smallbreak", "Small break"], ["\\medbreak", "Medium break"], ["\\bigbreak", "Large break"],
|
|
16
|
+
["\\hfill", "Horizontal fill"], ["\\hfil", "Horizontal fill"], ["\\vfill", "Vertical fill"], ["\\vfil", "Vertical fill"], ["\\thinspace", "Thin space"], ["\\negthinspace", "Negative thin space"],
|
|
17
|
+
["\\quad", "One em space"], ["\\qquad", "Two em spaces"], ["\\enspace", "En space"], ["\\enskip", "En space"], ["\\,", "Thin math space"], ["\\:", "Medium math space"], ["\\;", "Thick math space"], ["\\!", "Negative math space"],
|
|
18
|
+
["\\centering", "Center contents"], ["\\raggedright", "Left align contents"], ["\\raggedleft", "Right align contents"], ["\\raggedbottom", "Ragged page bottoms"], ["\\flushbottom", "Align page bottoms"],
|
|
19
|
+
["\\maketitle", "Render title"], ["\\tableofcontents", "Table of contents"], ["\\listoffigures", "List of figures"], ["\\listoftables", "List of tables"], ["\\appendix", "Appendix"],
|
|
20
|
+
["\\bfseries", "Bold series"], ["\\mdseries", "Medium series"], ["\\rmfamily", "Roman family"], ["\\sffamily", "Sans-serif family"], ["\\ttfamily", "Monospace family"], ["\\upshape", "Upright shape"], ["\\itshape", "Italic shape"], ["\\slshape", "Slanted shape"], ["\\scshape", "Small caps shape"], ["\\normalfont", "Normal font"],
|
|
21
|
+
["\\tiny", "Tiny font"], ["\\scriptsize", "Script-size font"], ["\\footnotesize", "Footnote-size font"], ["\\small", "Small font"], ["\\normalsize", "Normal font size"], ["\\large", "Large font"], ["\\Large", "Larger font"], ["\\LARGE", "Very large font"], ["\\huge", "Huge font"], ["\\Huge", "Largest font"],
|
|
22
|
+
["\\displaystyle", "Display math style"], ["\\textstyle", "Text math style"], ["\\scriptstyle", "Script math style"], ["\\scriptscriptstyle", "Script-script math style"], ["\\limits", "Place limits below"], ["\\nolimits", "Place limits beside"],
|
|
23
|
+
["\\sum", "Summation"], ["\\prod", "Product"], ["\\coprod", "Coproduct"], ["\\int", "Integral"], ["\\iint", "Double integral"], ["\\iiint", "Triple integral"], ["\\oint", "Contour integral"], ["\\bigcap", "Big intersection"], ["\\bigcup", "Big union"], ["\\bigvee", "Big logical or"], ["\\bigwedge", "Big logical and"],
|
|
24
|
+
["\\lim", "Limit operator"], ["\\limsup", "Limit superior"], ["\\liminf", "Limit inferior"], ["\\sin", "Sine operator"], ["\\cos", "Cosine operator"], ["\\tan", "Tangent operator"], ["\\cot", "Cotangent operator"], ["\\log", "Logarithm operator"], ["\\ln", "Natural logarithm"], ["\\exp", "Exponential operator"], ["\\max", "Maximum operator"], ["\\min", "Minimum operator"], ["\\sup", "Supremum operator"], ["\\inf", "Infimum operator"], ["\\det", "Determinant operator"], ["\\dim", "Dimension operator"], ["\\gcd", "Greatest common divisor"], ["\\ker", "Kernel operator"], ["\\Pr", "Probability operator"],
|
|
25
|
+
["\\ldots", "Ellipsis"], ["\\cdots", "Centered ellipsis"], ["\\vdots", "Vertical ellipsis"], ["\\ddots", "Diagonal ellipsis"], ["\\dotsb", "Binary ellipsis"], ["\\dotsc", "Punctuation ellipsis"], ["\\dotsm", "Math ellipsis"], ["\\dotso", "Other ellipsis"],
|
|
26
|
+
["\\alpha", "Greek letter alpha"], ["\\beta", "Greek letter beta"], ["\\gamma", "Greek letter gamma"], ["\\delta", "Greek letter delta"], ["\\epsilon", "Greek letter epsilon"], ["\\varepsilon", "Variant epsilon"], ["\\theta", "Greek letter theta"], ["\\vartheta", "Variant theta"], ["\\lambda", "Greek letter lambda"], ["\\mu", "Greek letter mu"], ["\\pi", "Greek letter pi"], ["\\varpi", "Variant pi"], ["\\rho", "Greek letter rho"], ["\\sigma", "Greek letter sigma"], ["\\tau", "Greek letter tau"], ["\\phi", "Greek letter phi"], ["\\varphi", "Variant phi"], ["\\omega", "Greek letter omega"], ["\\Gamma", "Greek letter Gamma"], ["\\Delta", "Greek letter Delta"], ["\\Theta", "Greek letter Theta"], ["\\Lambda", "Greek letter Lambda"], ["\\Xi", "Greek letter Xi"], ["\\Pi", "Greek letter Pi"], ["\\Sigma", "Greek letter Sigma"], ["\\Upsilon", "Greek letter Upsilon"], ["\\Phi", "Greek letter Phi"], ["\\Psi", "Greek letter Psi"], ["\\Omega", "Greek letter Omega"],
|
|
27
|
+
["\\pm", "Plus or minus"], ["\\mp", "Minus or plus"], ["\\times", "Multiplication sign"], ["\\div", "Division sign"], ["\\cdot", "Centered dot"], ["\\leq", "Less than or equal"], ["\\geq", "Greater than or equal"], ["\\neq", "Not equal"], ["\\approx", "Approximately equal"], ["\\equiv", "Equivalent"], ["\\sim", "Similar"], ["\\simeq", "Similar or equal"], ["\\propto", "Proportional"], ["\\in", "Set membership"], ["\\notin", "Not set membership"], ["\\subset", "Subset"], ["\\subseteq", "Subset or equal"], ["\\supset", "Superset"], ["\\supseteq", "Superset or equal"], ["\\cup", "Set union"], ["\\cap", "Set intersection"], ["\\emptyset", "Empty set"], ["\\varnothing", "Variant empty set"], ["\\forall", "For all"], ["\\exists", "There exists"], ["\\nexists", "There does not exist"], ["\\infty", "Infinity"], ["\\partial", "Partial derivative"], ["\\nabla", "Nabla"], ["\\angle", "Angle"], ["\\parallel", "Parallel"], ["\\perp", "Perpendicular"], ["\\mid", "Divides relation"], ["\\ldotp", "Low dot"],
|
|
28
|
+
["\\LaTeX", "LaTeX logo"], ["\\TeX", "TeX logo"], ["\\today", "Current date"], ["\\protect", "Protect command"], ["\\nocite", "Include uncited reference"], ["\\footnotemark", "Footnote marker"], ["\\hrule", "Horizontal rule"], ["\\verb", "Verbatim text"], ["\\verb*", "Verbatim text"]
|
|
29
|
+
];
|
|
8
30
|
// This is deliberately kept as metadata rather than hard-coded in the editor,
|
|
9
31
|
// so the same vocabulary is available to every client and can be extended later.
|
|
10
32
|
const standardCommands = [
|
|
@@ -22,13 +44,13 @@ const standardCommands = [
|
|
|
22
44
|
["\\parencite", "Parenthetical citation", "\\parencite{${key}}"], ["\\textcite", "Textual citation", "\\textcite{${key}}"],
|
|
23
45
|
["\\footnote", "Footnote", "\\footnote{${text}}"], ["\\marginpar", "Margin note", "\\marginpar{${text}}"],
|
|
24
46
|
["\\textbf", "Bold text", "\\textbf{${text}}"], ["\\textit", "Italic text", "\\textit{${text}}"], ["\\texttt", "Monospace text", "\\texttt{${text}}"],
|
|
25
|
-
["\\textrm", "Roman text", "\\textrm{${text}}"], ["\\textsf", "Sans-serif text", "\\textsf{${text}}"], ["\\textsc", "Small caps text", "\\textsc{${text}}"],
|
|
26
|
-
["\\emph", "Emphasized text", "\\emph{${text}}"], ["\\underline", "Underlined text", "\\underline{${text}}"], ["\\mbox", "Unbreakable text", "\\mbox{${text}}"],
|
|
47
|
+
["\\textrm", "Roman text", "\\textrm{${text}}"], ["\\textsf", "Sans-serif text", "\\textsf{${text}}"], ["\\textsc", "Small caps text", "\\textsc{${text}}"], ["\\textnormal", "Normal text", "\\textnormal{${text}}"], ["\\textup", "Upright text", "\\textup{${text}}"], ["\\textsl", "Slanted text", "\\textsl{${text}}"], ["\\textmd", "Medium text", "\\textmd{${text}}"],
|
|
48
|
+
["\\textsuperscript", "Superscript", "\\textsuperscript{${text}}"], ["\\textsubscript", "Subscript", "\\textsubscript{${text}}"], ["\\emph", "Emphasized text", "\\emph{${text}}"], ["\\underline", "Underlined text", "\\underline{${text}}"], ["\\mbox", "Unbreakable text", "\\mbox{${text}}"], ["\\fbox", "Framed text", "\\fbox{${text}}"], ["\\makebox", "Fixed-width box", "\\makebox[${width}][${position}]{${text}}"], ["\\parbox", "Paragraph box", "\\parbox[${position}][${height}][${inner}]{${width}}{${text}}"],
|
|
27
49
|
["\\textwidth", "Text width"], ["\\linewidth", "Current line width"], ["\\columnwidth", "Column width"], ["\\paperwidth", "Paper width"],
|
|
28
50
|
["\\include", "Include LaTeX file", "\\include{${file}}"], ["\\input", "Input LaTeX file", "\\input{${file}}"], ["\\includeonly", "Limit included files", "\\includeonly{${file}}"],
|
|
29
51
|
["\\includegraphics", "Include image", "\\includegraphics[width=${0.8}\\textwidth]{${file}}"], ["\\graphicspath", "Image search path", "\\graphicspath{{${path}/}}"],
|
|
30
52
|
["\\caption", "Caption", "\\caption{${caption}}"], ["\\captionof", "Caption outside float", "\\captionof{${type}}{${caption}}"],
|
|
31
|
-
[
|
|
53
|
+
...standardNoArgumentCommands.map(([label, detail]) => [label, detail]),
|
|
32
54
|
["\\linebreak", "Suggest line break"], ["\\pagebreak", "Suggest page break"], ["\\clearpage", "Flush floats and page break"], ["\\newpage", "Page break"], ["\\vspace", "Vertical space", "\\vspace{${length}}"],
|
|
33
55
|
["\\hspace", "Horizontal space", "\\hspace{${length}}"], ["\\hfill", "Horizontal fill"], ["\\vfill", "Vertical fill"], ["\\rule", "Rule", "\\rule{${width}}{${height}}"],
|
|
34
56
|
["\\ldots", "Ellipsis"], ["\\dots", "Ellipsis"], ["\\LaTeX", "LaTeX logo"], ["\\TeX", "TeX logo"], ["\\today", "Current date"],
|
|
@@ -38,9 +60,10 @@ const standardCommands = [
|
|
|
38
60
|
["\\newcounter", "Define counter", "\\newcounter{${name}}"], ["\\setcounter", "Set counter"], ["\\stepcounter", "Step counter"], ["\\value", "Counter value"],
|
|
39
61
|
["\\newtheorem", "Define theorem", "\\newtheorem{${name}}{${caption}}"], ["\\DeclareMathOperator", "Define math operator"], ["\\operatorname", "Math operator"],
|
|
40
62
|
["\\frac", "Fraction", "\\frac{${numerator}}{${denominator}}"], ["\\dfrac", "Display fraction", "\\dfrac{${numerator}}{${denominator}}"], ["\\tfrac", "Text fraction", "\\tfrac{${numerator}}{${denominator}}"],
|
|
41
|
-
["\\sqrt", "Square root", "\\sqrt[${index}]{${radicand}}"], ["\\
|
|
42
|
-
["\\mathbf", "Bold math", "\\mathbf{${symbol}}"], ["\\mathrm", "Roman math", "\\mathrm{${symbol}}"], ["\\mathit", "Italic math", "\\mathit{${symbol}}"],
|
|
43
|
-
["\\
|
|
63
|
+
["\\sqrt", "Square root", "\\sqrt[${index}]{${radicand}}"], ["\\binom", "Binomial", "\\binom{${numerator}}{${denominator}}"], ["\\pmod", "Parenthesized modulo", "\\pmod{${modulus}}"], ["\\bmod", "Binary modulo", "\\bmod ${modulus}"], ["\\mod", "Modulo", "\\mod ${modulus}"],
|
|
64
|
+
["\\mathbf", "Bold math", "\\mathbf{${symbol}}"], ["\\mathrm", "Roman math", "\\mathrm{${symbol}}"], ["\\mathit", "Italic math", "\\mathit{${symbol}}"], ["\\mathsf", "Sans-serif math", "\\mathsf{${symbol}}"], ["\\mathtt", "Monospace math", "\\mathtt{${symbol}}"], ["\\mathbb", "Blackboard math", "\\mathbb{${symbol}}"], ["\\mathcal", "Calligraphic math", "\\mathcal{${symbol}}"], ["\\mathscr", "Script math", "\\mathscr{${symbol}}"], ["\\boldsymbol", "Bold math symbol", "\\boldsymbol{${symbol}}"],
|
|
65
|
+
["\\hat", "Math accent", "\\hat{${symbol}}"], ["\\widehat", "Wide math accent", "\\widehat{${symbol}}"], ["\\bar", "Math bar", "\\bar{${symbol}}"], ["\\overline", "Math overline", "\\overline{${symbol}}"], ["\\vec", "Math vector", "\\vec{${symbol}}"], ["\\tilde", "Math tilde", "\\tilde{${symbol}}"], ["\\widetilde", "Wide math tilde", "\\widetilde{${symbol}}"], ["\\dot", "Math dot", "\\dot{${symbol}}"], ["\\ddot", "Math double dot", "\\ddot{${symbol}}"], ["\\overset", "Math overset", "\\overset{${annotation}}{${symbol}}"], ["\\underset", "Math underset", "\\underset{${annotation}}{${symbol}}"], ["\\overbrace", "Math overbrace", "\\overbrace{${symbol}}"], ["\\underbrace", "Math underbrace", "\\underbrace{${symbol}}"],
|
|
66
|
+
["\\left", "Scalable left delimiter"], ["\\right", "Scalable right delimiter"], ["\\middle", "Scalable middle delimiter"], ["\\text", "Text in math", "\\text{${text}}"],
|
|
44
67
|
["\\begin", "Begin environment", "\\begin{${environment}}"], ["\\end", "End environment", "\\end{${environment}}"],
|
|
45
68
|
["\\item", "List item", "\\item ${text}"],
|
|
46
69
|
["\\bibliography", "Bibliography database", "\\bibliography{${file}}"], ["\\bibliographystyle", "Bibliography style", "\\bibliographystyle{${style}}"],
|
|
@@ -88,7 +111,7 @@ function withoutComments(content) {
|
|
|
88
111
|
function commandItem(index, name, detail, source, apply) {
|
|
89
112
|
if (!name.startsWith("\\"))
|
|
90
113
|
name = `\\${name}`;
|
|
91
|
-
if (!/^\\[A-Za-z@][A-Za-z@0-9:_]
|
|
114
|
+
if (!/^\\(?:[A-Za-z@][A-Za-z@0-9:_]*(?:\*)?|[,;!:])$/.test(name))
|
|
92
115
|
return;
|
|
93
116
|
const existing = index.commands.get(name);
|
|
94
117
|
// A project definition is more useful than the generic built-in entry: it
|
|
@@ -5,7 +5,7 @@ import { performance } from "node:perf_hooks";
|
|
|
5
5
|
import { requireUser } from "../auth.js";
|
|
6
6
|
import { accessibleProject, canEdit } from "../projects.js";
|
|
7
7
|
import { resolveSourcePath, safeRelativePath } from "../files.js";
|
|
8
|
-
import { captureCompileSnapshot, compileProject, discardCompileSnapshot, publishCompileArtifacts, publishedCompileArtifacts } from "../compiler.js";
|
|
8
|
+
import { captureCompileSnapshot, cleanCompileArtifacts, cleanCompileCache, compileProject, discardCompileSnapshot, hasCompileCache, publishCompileArtifacts, publishedCompileArtifacts } from "../compiler.js";
|
|
9
9
|
import { parseCompileDiagnostics } from "../compileDiagnostics.js";
|
|
10
10
|
import { pdfToSource, sourceToPdf } from "../synctex.js";
|
|
11
11
|
import { availablePdf, compileMainFile, compilePdfUrl, isTextCompileArtifact, listCompileArtifacts, syncArtifacts } from "../compileArtifacts.js";
|
|
@@ -92,6 +92,53 @@ export function registerCompileRoutes(app, context) {
|
|
|
92
92
|
}
|
|
93
93
|
return { path: relative, content: fs.readFileSync(resolved, "utf8") };
|
|
94
94
|
});
|
|
95
|
+
app.post("/api/projects/:id/compile/clean", async (request, reply) => {
|
|
96
|
+
const user = requireUser(request, reply, db);
|
|
97
|
+
if (!user)
|
|
98
|
+
return;
|
|
99
|
+
const { id } = request.params;
|
|
100
|
+
const project = accessibleProject(db, id, user);
|
|
101
|
+
if (!project || !canEdit(project))
|
|
102
|
+
return apiError(reply, 403, "COMPILE_FORBIDDEN", "没有清理编译产物的权限");
|
|
103
|
+
const body = (request.body ?? {});
|
|
104
|
+
const mainFile = compileMainFile(config, id, project.main_file, body.mainFile);
|
|
105
|
+
if (!mainFile)
|
|
106
|
+
return apiError(reply, 400, "MAIN_DOCUMENT_INVALID", "所选文件不是有效的 LaTeX 主文档");
|
|
107
|
+
if (body.mode !== "cache" && body.mode !== "artifacts") {
|
|
108
|
+
return apiError(reply, 400, "REQUEST_INVALID", "清理模式无效");
|
|
109
|
+
}
|
|
110
|
+
const activeRun = db.prepare(`SELECT 1 AS active FROM compile_runs
|
|
111
|
+
WHERE project_id = ? AND main_file = ? AND status IN ('queued', 'running') LIMIT 1`).get(id, mainFile);
|
|
112
|
+
if (activeRun)
|
|
113
|
+
return apiError(reply, 409, "COMPILE_CLEAN_BUSY", "当前主文档正在编译,请稍后再清理");
|
|
114
|
+
const requestedBy = { id: user.id, username: user.username, name: user.display_name };
|
|
115
|
+
if (body.mode === "cache") {
|
|
116
|
+
cleanCompileCache(config, id, mainFile);
|
|
117
|
+
collaboration.signalCompileState(id, {
|
|
118
|
+
mainFile,
|
|
119
|
+
runId: `clean-${randomUUID()}`,
|
|
120
|
+
status: "cleaned",
|
|
121
|
+
cleanMode: "cache",
|
|
122
|
+
requestedBy,
|
|
123
|
+
updatedAt: now()
|
|
124
|
+
});
|
|
125
|
+
return { ok: true, mode: "cache", mainFile, retainedPdf: true };
|
|
126
|
+
}
|
|
127
|
+
const runs = db.prepare(`SELECT id FROM compile_runs
|
|
128
|
+
WHERE project_id = ? AND main_file = ? AND status NOT IN ('queued', 'running')`).all(id, mainFile);
|
|
129
|
+
cleanCompileArtifacts(config, id, mainFile, project.main_file, runs.map((run) => run.id));
|
|
130
|
+
db.prepare("DELETE FROM compile_runs WHERE project_id = ? AND main_file = ? AND status NOT IN ('queued', 'running')").run(id, mainFile);
|
|
131
|
+
pruneCompileRuns(id);
|
|
132
|
+
collaboration.signalCompileState(id, {
|
|
133
|
+
mainFile,
|
|
134
|
+
runId: `clean-${randomUUID()}`,
|
|
135
|
+
status: "cleaned",
|
|
136
|
+
cleanMode: "artifacts",
|
|
137
|
+
requestedBy,
|
|
138
|
+
updatedAt: now()
|
|
139
|
+
});
|
|
140
|
+
return { ok: true, mode: "artifacts", mainFile, retainedPdf: false };
|
|
141
|
+
});
|
|
95
142
|
app.get("/api/projects/:id/sync/pdf", async (request, reply) => {
|
|
96
143
|
const user = requireUser(request, reply, db);
|
|
97
144
|
if (!user)
|
|
@@ -179,7 +226,8 @@ export function registerCompileRoutes(app, context) {
|
|
|
179
226
|
const activeRun = db.prepare(`SELECT 1 AS active FROM compile_runs
|
|
180
227
|
WHERE project_id = ? AND main_file = ? AND status IN ('queued', 'running') LIMIT 1`).get(id, mainFile);
|
|
181
228
|
if (!activeRun && published && publishedRun?.status === "succeeded"
|
|
182
|
-
&& latestRun?.id === publishedRun.id && snapshot.revision === published.revision
|
|
229
|
+
&& latestRun?.id === publishedRun.id && snapshot.revision === published.revision
|
|
230
|
+
&& hasCompileCache(config, id, mainFile)) {
|
|
183
231
|
discardCompileSnapshot(snapshot);
|
|
184
232
|
const requestMs = performance.now() - requestStartedAt;
|
|
185
233
|
metrics.record("compile.request", requestMs);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "texlite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A lightweight local-first collaborative LaTeX workspace",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -69,14 +69,13 @@
|
|
|
69
69
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
70
70
|
"@replit/codemirror-vim": "^6.4.0",
|
|
71
71
|
"better-sqlite3": "^13.0.2",
|
|
72
|
-
"dictionary-en": "^4.0.0",
|
|
73
72
|
"diff-match-patch": "^1.0.5",
|
|
74
73
|
"fastify": "^5.4.0",
|
|
74
|
+
"harper.js": "^2.7.0",
|
|
75
75
|
"i18next": "^25.3.2",
|
|
76
76
|
"i18next-browser-languagedetector": "^8.2.0",
|
|
77
77
|
"lib0": "^0.2.117",
|
|
78
78
|
"lucide-react": "^0.468.0",
|
|
79
|
-
"nspell": "^2.1.5",
|
|
80
79
|
"pdfjs-dist": "^6.2.108",
|
|
81
80
|
"pm2": "^6.0.14",
|
|
82
81
|
"react": "^19.1.1",
|