collabmd 0.1.45 → 0.1.46
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/README.md +39 -19
- package/bin/collabmd.js +27 -1
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CYM5nxUi.js +1 -0
- package/dist/client/assets/browser-utils--H_DkyEF.js +1 -0
- package/dist/client/assets/chunk-ERSWUTIN-hHdvnhaY.js +41 -0
- package/dist/client/assets/comment-thread-store-CdcwrObc.js +1 -0
- package/dist/client/assets/comment-threads-9J6Em-_y.js +4 -0
- package/dist/client/assets/comment-ui-shared-BC4pt4w8.js +7 -0
- package/dist/client/assets/dist-B5HTH2BO.js +1 -0
- package/dist/client/assets/{dist-DhLvC8DS.js → dist-B5Jf1SLZ.js} +2 -2
- package/dist/client/assets/{dist-CnL7Y2sM.js → dist-BDt3rz7l.js} +1 -1
- package/dist/client/assets/dist-BEwK4csK.js +1 -0
- package/dist/client/assets/dist-BKW4PALF.js +1 -0
- package/dist/client/assets/dist-BWcHHOx-.js +1 -0
- package/dist/client/assets/{dist-CYZmbokb.js → dist-BalScOwZ.js} +9 -9
- package/dist/client/assets/dist-Bf8f4HmM.js +6 -0
- package/dist/client/assets/dist-BjHJDtyU.js +1 -0
- package/dist/client/assets/dist-Bm6pf9Bl.js +1 -0
- package/dist/client/assets/dist-BoVBdSjk.js +1 -0
- package/dist/client/assets/dist-CC5IrQMG.js +1 -0
- package/dist/client/assets/{dist-C11hHqv_.js → dist-CCP_AKPG.js} +12 -12
- package/dist/client/assets/dist-CZgOmxK6.js +1 -0
- package/dist/client/assets/{dist-D2AQ-76g.js → dist-Cqhv5LAk.js} +1 -1
- package/dist/client/assets/{dist-MvcV4190.js → dist-D-Z6FLem.js} +1 -1
- package/dist/client/assets/dist-DLMiIsNC.js +11 -0
- package/dist/client/assets/{dist-3bgCp7iY.js → dist-DZKu9Cs8.js} +1 -1
- package/dist/client/assets/dist-DaYdhcVs.js +1 -0
- package/dist/client/assets/dist-FWJsgGYI.js +1 -0
- package/dist/client/assets/dist-Kh2COra_.js +1 -0
- package/dist/client/assets/{dist-JYEDRrPT.js → dist-PDZ3bSqm.js} +1 -1
- package/dist/client/assets/{dist-Drp8uu5J.js → dist-TjMh8n1u.js} +1 -1
- package/dist/client/assets/{dist-DGb27Dl6.js → dist-dtAdgL39.js} +1 -1
- package/dist/client/assets/dist-jv4WdPnK.js +9 -0
- package/dist/client/assets/{dist-DK5Gs2wa.js → dist-rGBmqxC7.js} +1 -1
- package/dist/client/assets/{dist-DCzuX1AF.js → dist-shrl6K_0.js} +1 -1
- package/dist/client/assets/dist-tOaegDYS2.js +1 -0
- package/dist/client/assets/dist-ule2n6ak.js +1 -0
- package/dist/client/assets/dist-xElaxbia.js +1 -0
- package/dist/client/assets/{drawio-editor-ClE8jpoG.js → drawio-editor-FzY1cVw8.js} +1 -1
- package/dist/client/assets/drawioEditor-BjiKJqlc.js +2 -0
- package/dist/client/assets/editor-session-Cm9ekeNo.js +54 -0
- package/dist/client/assets/embedded-editor-base-CwTkk911.css +1 -0
- package/dist/client/assets/{preview-render-profile-BZzGSg6K.js → es-DXGV3RHd.js} +13 -13
- package/dist/client/assets/excalidraw-editor-B46zsTFI.js +9 -0
- package/dist/client/assets/{excalidraw-editor-CyGwOhLz.css → excalidraw-editor-Bt7gMSoh.css} +1 -1
- package/dist/client/assets/excalidraw-scene-BOben2gr.js +1 -0
- package/dist/client/assets/excalidrawEditor-CT2pHq7e.js +2 -0
- package/dist/client/assets/{export-page-CUfws2T-.js → export-page-A7NHawJY.js} +3 -3
- package/dist/client/assets/exportDocument-B2L8fe34.css +1 -0
- package/dist/client/assets/exportDocument-BaqcOAAW.js +2 -0
- package/dist/client/assets/file-icon-svg-CQAjlWjK.js +1 -0
- package/dist/client/assets/file-kind-BbqgGzpc.js +1 -0
- package/dist/client/assets/image-QXRYHUNP-SWEV8H0J.js +1 -0
- package/dist/client/assets/index-ClbjMBVt.js +2 -0
- package/dist/client/assets/index-Pum8ET-5.css +1 -0
- package/dist/client/assets/main-B3uPyqic.js +893 -0
- package/dist/client/assets/pdf-BL9Pr2D5.js +54 -0
- package/dist/client/assets/pdf.worker-CLesOks4.mjs +64857 -0
- package/dist/client/assets/pdf_viewer-FVkkThnz.js +551 -0
- package/dist/client/assets/{preview-render-compiler-BSaQO_p3.js → preview-render-compiler-BD-XhkS2.js} +2 -2
- package/dist/client/assets/preview-render-compiler-BKedaMxx.js +1 -0
- package/dist/client/assets/preview-render-profile-CE-5rVTC.js +1 -0
- package/dist/client/assets/{preview-render-worker-B42LWvrD.js → preview-render-worker-CiPqc9n6.js} +20 -20
- package/dist/client/assets/prod-Dy20X7eT.js +1 -0
- package/dist/client/assets/prod-hnwh7wRM.js +177 -0
- package/dist/client/assets/quick-switcher-controller-fqOYR7qR.js +12 -0
- package/dist/client/assets/runtime-config-BLrEKVO1.js +1 -0
- package/dist/client/assets/vault-api-client-_LpV1aIn.js +6 -0
- package/dist/client/drawio-editor.html +1 -1
- package/dist/client/excalidraw-editor.html +1 -1
- package/dist/client/export-document.html +2 -2
- package/dist/client/index.html +15 -3
- package/docker-compose.yml +42 -0
- package/package.json +17 -4
- package/scripts/local-structurizr-compose.mjs +101 -0
- package/src/client/app/index.html +13 -1
- package/src/client/application/app-shell/comments-feature.js +19 -10
- package/src/client/application/app-shell/git-feature.js +12 -0
- package/src/client/application/app-shell/presence-feature.js +2 -1
- package/src/client/application/app-shell/ui-feature-shell.js +45 -2
- package/src/client/application/app-shell/ui-feature-toolbar.js +2 -52
- package/src/client/application/app-shell/workspace-feature.js +5 -1
- package/src/client/application/app-shell-elements.js +2 -0
- package/src/client/application/pdf-preview-controller.js +992 -0
- package/src/client/application/structurizr-preview-controller.js +395 -0
- package/src/client/application/workspace-coordinator.js +25 -3
- package/src/client/application/workspace-preview-controller.js +91 -2
- package/src/client/bootstrap/collabmd-app-shell.js +31 -5
- package/src/client/browser-utils.js +44 -0
- package/src/client/domain/runtime-paths.js +26 -5
- package/src/client/domain/structurizr-language.js +99 -0
- package/src/client/domain/vault-paths.js +48 -0
- package/src/client/excalidraw-editor.js +833 -51
- package/src/client/infrastructure/browser-preferences-port.js +10 -0
- package/src/client/infrastructure/comment-thread-store.js +34 -13
- package/src/client/infrastructure/editor-session.js +10 -0
- package/src/client/infrastructure/editor-view-adapter.js +46 -2
- package/src/client/infrastructure/excalidraw-room-client.js +103 -14
- package/src/client/infrastructure/git-api-client.js +5 -0
- package/src/client/infrastructure/vault-api-client.js +26 -0
- package/src/client/presentation/comment-ui/comment-ui-shared.js +4 -0
- package/src/client/presentation/drawio-embed-controller.js +9 -102
- package/src/client/presentation/drawio-viewer.js +118 -0
- package/src/client/presentation/excalidraw-embed-controller.js +74 -8
- package/src/client/presentation/file-action-controller.js +102 -0
- package/src/client/presentation/file-explorer-view.js +10 -29
- package/src/client/presentation/file-icon-svg.js +40 -0
- package/src/client/presentation/file-tree-state.js +2 -0
- package/src/client/presentation/git-diff-view-controller.js +731 -3
- package/src/client/presentation/outline-controller.js +86 -0
- package/src/client/presentation/quick-switcher-controller.js +25 -13
- package/src/client/presentation/quick-switcher-text-search.js +1 -1
- package/src/client/styles/components/navigation.css +11 -1
- package/src/client/styles/features/diagram-preview.css +76 -0
- package/src/client/styles/features/diff-special.css +309 -0
- package/src/client/styles/foundation/themes.css +6 -0
- package/src/client/styles/layout/shell.css +358 -1
- package/src/client/styles/layout/view-modes.css +1 -0
- package/src/client/styles/style.css +1 -0
- package/src/client/styles/surfaces/excalidraw-editor.css +294 -0
- package/src/domain/comment-threads.js +63 -5
- package/src/domain/excalidraw-room-codec.js +6 -4
- package/src/domain/file-kind.js +35 -3
- package/src/server/config/env.js +79 -0
- package/src/server/create-app-server.js +12 -0
- package/src/server/domain/bases/base-definition.js +1 -1
- package/src/server/domain/bases/base-expression-runtime.js +1 -4
- package/src/server/domain/bases/base-index-snapshot-store.js +1 -4
- package/src/server/domain/bases/base-transform.js +1 -4
- package/src/server/domain/collaboration/collaboration-room.js +55 -21
- package/src/server/domain/comment-overview.js +15 -5
- package/src/server/domain/ripgrep-search-service.js +4 -0
- package/src/server/infrastructure/git/diff-service.js +5 -0
- package/src/server/infrastructure/git/git-service.js +21 -0
- package/src/server/infrastructure/git/history-service.js +54 -0
- package/src/server/infrastructure/git/image-mime.js +14 -0
- package/src/server/infrastructure/git/untracked-files.js +28 -1
- package/src/server/infrastructure/http/create-git-api-query-handler.js +31 -1
- package/src/server/infrastructure/http/create-request-handler.js +16 -23
- package/src/server/infrastructure/http/create-static-handler.js +5 -2
- package/src/server/infrastructure/http/create-structurizr-api-handler.js +145 -0
- package/src/server/infrastructure/http/create-vault-api-command-handler.js +66 -4
- package/src/server/infrastructure/http/create-vault-api-query-handler.js +51 -3
- package/src/server/infrastructure/http/http-request-helpers.js +22 -0
- package/src/server/infrastructure/persistence/path-utils.js +1 -1
- package/src/server/infrastructure/persistence/vault-file-store.js +3 -0
- package/src/server/infrastructure/structurizr/structurizr-embed.css +43 -0
- package/src/server/infrastructure/structurizr/structurizr-workspace-service.js +374 -0
- package/src/server/infrastructure/websocket/attach-collaboration-gateway.js +4 -22
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CbSISC1w.js +0 -1
- package/dist/client/assets/browser-utils-CKtI_rZl.js +0 -1
- package/dist/client/assets/chunk-ERSWUTIN-ME9RVNel.js +0 -41
- package/dist/client/assets/comment-threads-DDegmaFO.js +0 -4
- package/dist/client/assets/dist-BH87JoHx.js +0 -11
- package/dist/client/assets/dist-BIkPRZO7.js +0 -6
- package/dist/client/assets/dist-BqCsFpx4.js +0 -1
- package/dist/client/assets/dist-C2JHCt8U.js +0 -1
- package/dist/client/assets/dist-CZjTtmTu.js +0 -1
- package/dist/client/assets/dist-Cig4Ov_X.js +0 -1
- package/dist/client/assets/dist-CqQEC8gR.js +0 -1
- package/dist/client/assets/dist-D4Rzqod1.js +0 -1
- package/dist/client/assets/dist-D5sn-mwk.js +0 -9
- package/dist/client/assets/dist-D7G-n87v.js +0 -1
- package/dist/client/assets/dist-DRqOrNgN.js +0 -1
- package/dist/client/assets/dist-DetkMS_4.js +0 -1
- package/dist/client/assets/dist-Dp6FVkQ1.js +0 -1
- package/dist/client/assets/dist-DtbjNB28.js +0 -1
- package/dist/client/assets/dist-DyrYcvpo2.js +0 -1
- package/dist/client/assets/dist-GrygD2zE.js +0 -1
- package/dist/client/assets/dist-HmEpWUow.js +0 -1
- package/dist/client/assets/dist-RTa81Km7.js +0 -1
- package/dist/client/assets/drawioEditor-CXaTM9Ds.js +0 -2
- package/dist/client/assets/editor-session-GTwZS6Df.js +0 -16
- package/dist/client/assets/embedded-editor-base-CiNEc8vy.css +0 -1
- package/dist/client/assets/excalidraw-editor-BZVM81BO.js +0 -9
- package/dist/client/assets/excalidraw-scene-C8n9CHsK.js +0 -176
- package/dist/client/assets/excalidrawEditor-DxJanCze.js +0 -2
- package/dist/client/assets/exportDocument-BCuxqXDY.js +0 -2
- package/dist/client/assets/exportDocument-DV-OX74E.css +0 -1
- package/dist/client/assets/file-kind-BKPOIRPE.js +0 -1
- package/dist/client/assets/image-QXRYHUNP-lfTHE3bb.js +0 -1
- package/dist/client/assets/index-C3YnUgMD.css +0 -1
- package/dist/client/assets/index-D2wXraJ1.js +0 -2
- package/dist/client/assets/main-DtiGz7uI.js +0 -795
- package/dist/client/assets/preview-render-compiler-csGM5ZFz.js +0 -1
- package/dist/client/assets/quick-switcher-controller-shW_6vPY.js +0 -12
- package/dist/client/assets/room-CQLoqtMo.js +0 -1
- package/dist/client/assets/runtime-config-DSGmGE9V.js +0 -1
- package/dist/client/assets/vault-api-client-C6bcGwy-.js +0 -5
- package/src/server/domain/collaboration/room-persistence-controller.js +0 -80
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import{g as e}from"./file-kind-BbqgGzpc.js";import{n as t}from"./vault-utils-wdQ0RNR9.js";import{t as n}from"./file-icon-svg-CQAjlWjK.js";var r=50;function i(e=0,{truncated:t=!1}={}){let n=Number(e)||0;return`${n}${t?`+`:``} ${n===1?`match`:`matches`}`}function a(e={}){let t=[];return(e.files??[]).forEach(e=>{(e.snippets??[]).forEach(n=>{t.push({column:n.column,file:e.file,kind:e.kind,line:n.line,matchLength:Math.max((n.matchEnd??0)-(n.matchStart??0),0),snippet:n})})}),t}var o=class{constructor({debounceMs:e=300,resultLimit:t=r}={}){this.debounceMs=e,this.resultLimit=t,this.controller=null,this.timer=null,this.token=0}abort({invalidate:e=!0}={}){clearTimeout(this.timer),this.timer=null,e&&(this.token+=1),this.controller?.abort(),this.controller=null}schedule({isActive:e=()=>!0,onResults:t,onState:n,query:r=``,searchConfig:i={},searchText:a=null}={}){let o=Math.max(Number(i.minQueryLength)||2,1);if(this.abort(),!i.available){n?.(`Global text search requires ripgrep on the server.`);return}if(!a){n?.(`Global text search is unavailable.`);return}if(r.length<o){n?.(`Type at least ${o} characters to search file text.`);return}n?.(`Searching...`),this.timer=setTimeout(()=>{this.run({isActive:e,onResults:t,onState:n,query:r,searchText:a})},this.debounceMs)}async run({isActive:e=()=>!0,onResults:t,onState:n,query:r=``,searchText:i}={}){let a=this.token+1;this.token=a;let o=new AbortController;this.controller=o;let s=()=>e()&&a===this.token&&this.controller===o;try{let e=await i({limit:this.resultLimit,query:r,signal:o.signal});s()&&t?.(e,r)}catch(e){if(e?.name===`AbortError`||!s())return;n?.(e?.body?.error||e?.message||`Failed to search file text.`)}finally{this.controller===o&&(this.controller=null)}}},s=30,c=2**53-1,l=`quick-switcher-file-`,u=`quick-switcher-text-`;function d(e){return String(e??``).split(`/`).pop()||String(e??``)}function f(t){let n=e(t),r=n.split(`/`).pop()||n,i=d(t);return{displayName:n,fileName:r,filePath:t,lowerDisplayName:n.toLowerCase(),lowerFileName:r.toLowerCase(),lowerPath:String(t).toLowerCase(),lowerRawFileName:i.toLowerCase()}}function p(t){let n=e(t);return n.split(`/`).pop()||n}function m(t){let n=e(t);return n.includes(`/`)?n.substring(0,n.lastIndexOf(`/`)):``}function h(e,t){return Array.from({length:t},(t,n)=>e+n)}function g(e,t){let n=[],r=0,i=0,a=0;for(let o=0;o<e.length&&r<t.length;o+=1){if(e[o]!==t[r]){a=0;continue}r+=1,n.push(o),a+=1,i+=a,(o===0||e[o-1]===`/`||e[o-1]===`-`||e[o-1]===`_`||e[o-1]===` `)&&(i+=5)}return r===t.length&&i>0?{indices:n,score:i}:null}function _(e,t){let n=e.lowerFileName.indexOf(t);if(n>=0)return{indices:h(n+(e.displayName.length-e.fileName.length),t.length),score:100+1/e.fileName.length};let r=e.lowerDisplayName.indexOf(t);if(r>=0)return{indices:h(r,t.length),score:50+1/e.displayName.length};let i=e.lowerRawFileName.indexOf(t);if(i>=0)return{indices:h(i+(e.displayName.length-e.fileName.length),t.length),score:40+1/e.lowerRawFileName.length};let a=e.lowerPath.indexOf(t);return a>=0?{indices:h(a,t.length),score:25+1/e.lowerPath.length}:g(e.lowerDisplayName,t)}function v(e,t=[]){let n=e.displayName.length-e.fileName.length;return{dirPath:t.filter(e=>e<n),fileName:t.filter(e=>e>=n).map(e=>e-n)}}function y(e,n=[]){if(n.length===0)return t(e);let r=new Set(n),i=``,a=null,o=0,s=n=>{i+=t(e.slice(o,a)),i+=`<mark>${t(e.slice(a,n+1))}</mark>`,o=n+1,a=null};for(let t=0;t<e.length;t+=1){if(!r.has(t)){a!==null&&s(t-1);continue}a===null&&(a=t)}return a!==null&&s(e.length-1),i+=t(e.slice(o)),i}var b=class{constructor({getFileList:e,getFileMetadata:t=()=>[],getRecentFiles:n=()=>[],getSearchConfig:r=()=>({}),onFileSelect:i,onTextMatchSelect:a=null,searchDebounceMs:s=300,searchText:c=null}){this.getFileList=e,this.getFileMetadata=t,this.getRecentFiles=n,this.getSearchConfig=r,this.onFileSelect=i,this.onTextMatchSelect=a,this.searchDebounceMs=s,this.searchText=c,this.overlay=document.getElementById(`quickSwitcher`),this.input=document.getElementById(`quickSwitcherInput`),this.resultsList=document.getElementById(`quickSwitcherResults`),this.hint=document.getElementById(`quickSwitcherHint`),this.scope=document.getElementById(`quickSwitcherScope`),this.modeTabs=Array.from(document.querySelectorAll?.(`[data-qs-mode]`)??[]),this.filteredFiles=[],this.fileMatches=new Map,this.fileMatchCount=0,this.fileResultsTruncated=!1,this.fileCorpus=[],this.lastFileListRef=null,this.selectedIndex=0,this.selectedTextIndex=0,this.isOpen=!1,this.previousActiveElement=null,this.mode=`files`,this.textResults=null,this.textResultItems=[],this.textSearchRunner=new o({debounceMs:this.searchDebounceMs}),this.bindEvents()}bindEvents(){this.overlay?.addEventListener(`mousedown`,e=>{e.target===this.overlay&&this.close()}),this.modeTabs.forEach(e=>{e.addEventListener(`click`,()=>{this.setMode(e.dataset.qsMode===`text`?`text`:`files`,{preserveInput:!0})})}),this.input?.addEventListener(`input`,()=>{this.handleInput()}),this.input?.addEventListener(`keydown`,e=>{switch(e.key){case`ArrowDown`:e.preventDefault(),this.moveSelection(1);break;case`ArrowUp`:e.preventDefault(),this.moveSelection(-1);break;case`Enter`:e.preventDefault(),this.confirmSelection();break;case`Escape`:e.preventDefault(),this.close();break;case`Tab`:e.preventDefault(),this.modeTabs.length>1&&!e.shiftKey?this.setMode(this.mode===`files`?`text`:`files`,{preserveInput:!0}):this.moveSelection(e.shiftKey?-1:1)}})}open(){this.overlay&&(this.previousActiveElement=document.activeElement,this.isOpen=!0,this.input.value=``,this.selectedIndex=0,this.selectedTextIndex=0,this.overlay.classList.add(`visible`),this.overlay.setAttribute(`aria-hidden`,`false`),this.setMode(`files`,{preserveInput:!0}),this._focusAfterTransition())}_focusAfterTransition(){this._cancelPendingFocus();let e=()=>{this._focusCleanup=null,this.input?.focus(),this.isOpen&&this.input&&document.activeElement!==this.input&&setTimeout(()=>this.input?.focus(),50)},t=r=>{(r.propertyName===`visibility`||r.propertyName===`opacity`)&&(this.overlay.removeEventListener(`transitionend`,t),clearTimeout(n),e())};this.overlay.addEventListener(`transitionend`,t);let n=setTimeout(()=>{this.overlay.removeEventListener(`transitionend`,t),e()},160);this._focusCleanup=()=>{this.overlay.removeEventListener(`transitionend`,t),clearTimeout(n)}}_cancelPendingFocus(){this._focusCleanup&&=(this._focusCleanup(),null)}close({restoreFocus:e=!0}={}){if(!this.overlay)return;let t=this.previousActiveElement;this._cancelPendingFocus(),this.abortTextSearch(),this.isOpen=!1,this.overlay.classList.remove(`visible`),this.overlay.setAttribute(`aria-hidden`,`true`),this.input.value=``,this.resultsList.innerHTML=``,this.setActiveDescendant(``),this.previousActiveElement=null,e&&t?.focus?.()}toggle(){this.isOpen?this.close():this.open()}setMode(e=`files`,{preserveInput:t=!1}={}){let n=e===`text`?`text`:`files`;this.mode=n,this.selectedIndex=0,this.selectedTextIndex=0,!t&&this.input&&(this.input.value=``),this.modeTabs.forEach(e=>{let t=e.dataset.qsMode===n;e.classList.toggle(`active`,t),e.setAttribute(`aria-selected`,t?`true`:`false`)}),this.scope?.classList.toggle(`hidden`,n!==`text`),this.scope?.setAttribute(`aria-hidden`,n===`text`?`false`:`true`),this.input&&(this.input.placeholder=n===`text`?`Search text in files...`:`Search files...`,this.input.setAttribute(`aria-label`,n===`text`?`Search text in files`:`Search files`)),this.handleInput()}handleInput(){if(this.mode===`text`){this.scheduleTextSearch();return}this.abortTextSearch(),this.filterFiles()}filterFiles(){let e=this.input?.value.trim().toLowerCase()??``,t=this.getFileList?.()??[];t!==this.lastFileListRef&&(this.lastFileListRef=t,this.fileCorpus=t.map(e=>f(e))),this.fileMatches.clear();let n=this.getFileMetadata?.()??[],r=new Map((Array.isArray(n)?n:[]).map(e=>[e?.path,Number.isFinite(Number(e?.mtimeMs))?Number(e.mtimeMs):0])),i=this.getRecentFiles?.()??[],a=new Map((Array.isArray(i)?i:[]).map((e,t)=>[e,t]));if(!e)this.filteredFiles=[...this.fileCorpus].sort((e,t)=>{let n=(r.get(t.filePath)??0)-(r.get(e.filePath)??0);return n===0?(a.get(e.filePath)??c)-(a.get(t.filePath)??c):n}).slice(0,s).map(e=>e.filePath),this.fileMatchCount=this.fileCorpus.length;else{let t=[],n=0;this.fileCorpus.forEach(r=>{let i=_(r,e);if(!i)return;n+=1,this.fileMatches.set(r.filePath,i);let o={filePath:r.filePath,recentRank:a.get(r.filePath)??c,score:i.score},l=!1;for(let e=0;e<t.length;e+=1){let n=t[e];if(o.score>n.score||o.score===n.score&&(o.recentRank<n.recentRank||o.recentRank===n.recentRank&&r.lowerPath<String(n.filePath).toLowerCase())){t.splice(e,0,o),l=!0;break}}!l&&t.length<s&&t.push(o),t.length>s&&(t.length=s)}),this.filteredFiles=t.map(e=>e.filePath),this.fileMatchCount=n}this.fileResultsTruncated=this.fileMatchCount>s,this.selectedIndex=0,this.renderResults(e)}renderResults(e){if(!this.resultsList)return;if(this.resultsList.innerHTML=``,this.filteredFiles.length===0){this.setActiveDescendant(``),this.hint&&(this.hint.textContent=e?`No files found`:`No files in vault`,this.hint.classList.remove(`hidden`));return}if(this.hint){let t=this.fileResultsTruncated?e?`Showing the top ${s} of ${this.fileMatchCount} matches. Refine the query to narrow results.`:`Showing the first ${s} of ${this.fileMatchCount} files.`:``;this.hint.textContent=t,this.hint.classList.toggle(`hidden`,!t)}let t=document.createDocumentFragment();this.filteredFiles.forEach((e,r)=>{let i=document.createElement(`button`);i.type=`button`,i.className=`qs-result-item`,r===this.selectedIndex&&i.classList.add(`selected`),i.id=`${l}${r}`,i.setAttribute(`role`,`option`),i.setAttribute(`aria-selected`,r===this.selectedIndex?`true`:`false`),i.dataset.index=String(r);let a=d(e),o=m(e),s=this.fileMatches.get(e),c=this.fileCorpus.find(t=>t.filePath===e),u=c?v(c,s?.indices):{dirPath:[],fileName:[]};i.innerHTML=`
|
|
2
|
+
${n(e,{className:`qs-result-icon`})}
|
|
3
|
+
<span class="qs-result-name">${y(a,u.fileName)}</span>
|
|
4
|
+
${o?`<span class="qs-result-path">${y(o,u.dirPath)}</span>`:``}
|
|
5
|
+
`,i.addEventListener(`click`,()=>{this.selectedIndex=r,this.confirmSelection()}),i.addEventListener(`mouseenter`,()=>{this.selectedIndex=r,this.updateSelection()}),t.appendChild(i)}),this.resultsList.appendChild(t),this.updateSelection()}abortTextSearch({invalidate:e=!0}={}){this.textSearchRunner.abort({invalidate:e})}scheduleTextSearch(){let e=this.input?.value.trim()??``,t=this.getSearchConfig?.()??{};this.textResults=null,this.textResultItems=[],this.textSearchRunner.schedule({isActive:()=>this.isOpen&&this.mode===`text`,onResults:(e,t)=>{this.textResults=e,this.textResultItems=a(e),this.selectedTextIndex=0,this.renderTextResults(t)},onState:e=>this.renderTextState(e),query:e,searchConfig:t,searchText:this.searchText})}renderTextState(e){this.resultsList&&(this.resultsList.innerHTML=``,this.resultsList.setAttribute(`aria-busy`,e===`Searching...`?`true`:`false`),this.setActiveDescendant(``),this.hint&&(this.hint.textContent=e,this.hint.classList.remove(`hidden`)))}renderTextResults(e=``){if(!this.resultsList)return;if(this.resultsList.innerHTML=``,this.resultsList.setAttribute(`aria-busy`,`false`),!this.textResults?.files?.length||this.textResultItems.length===0){this.setActiveDescendant(``),this.renderTextState(e?`No text matches found`:`Type to search file text`);return}if(this.hint){let e=this.textResults.truncated?`Showing partial results: ${this.textResults.files.length} files and ${i(this.textResults.matchCount,{truncated:!0})}. Refine the query to narrow results.`:``;this.hint.textContent=e,this.hint.classList.toggle(`hidden`,!e)}let n=document.createDocumentFragment(),r=0;this.textResults.files.forEach(e=>{let a=document.createElement(`section`);a.className=`qs-text-group`,a.setAttribute(`role`,`group`),a.setAttribute(`aria-label`,e.file);let o=document.createElement(`div`);o.className=`qs-text-group-header`,o.innerHTML=`
|
|
6
|
+
<span class="qs-text-file-name">${t(p(e.file))}</span>
|
|
7
|
+
<span class="qs-text-file-meta">${t(m(e.file))}</span>
|
|
8
|
+
<span class="qs-text-count">${t(i(e.matchCount,{truncated:e.truncated}))}</span>
|
|
9
|
+
`,a.appendChild(o),(e.snippets??[]).forEach(e=>{let n=r,i=document.createElement(`button`);i.type=`button`,i.className=`qs-text-item`,n===this.selectedTextIndex&&i.classList.add(`selected`),i.id=`${u}${n}`,i.setAttribute(`role`,`option`),i.setAttribute(`aria-selected`,n===this.selectedTextIndex?`true`:`false`),i.dataset.textIndex=String(n),i.innerHTML=`
|
|
10
|
+
<span class="qs-text-line">L${t(String(e.line??1))}</span>
|
|
11
|
+
<span class="qs-text-snippet">${this.highlightSnippet(e)}</span>
|
|
12
|
+
`,i.addEventListener(`click`,()=>{this.selectedTextIndex=n,this.confirmSelection()}),i.addEventListener(`mouseenter`,()=>{this.selectedTextIndex=n,this.updateSelection()}),a.appendChild(i),r+=1}),n.appendChild(a)}),this.resultsList.appendChild(n),this.updateSelection()}highlightSnippet(e={}){let n=String(e.text??``),r=Math.min(Math.max(Number(e.matchStart)||0,0),n.length),i=Math.min(Math.max(Number(e.matchEnd)||r,r),n.length);return`${t(n.slice(0,r))}<mark>${t(n.slice(r,i))}</mark>${t(n.slice(i))}`}moveSelection(e){if(this.mode===`text`){if(this.textResultItems.length===0)return;this.selectedTextIndex=(this.selectedTextIndex+e+this.textResultItems.length)%this.textResultItems.length,this.updateSelection();return}this.filteredFiles.length!==0&&(this.selectedIndex=(this.selectedIndex+e+this.filteredFiles.length)%this.filteredFiles.length,this.updateSelection())}updateSelection(){if(!this.resultsList)return;if(this.mode===`text`){let e=this.resultsList.querySelectorAll(`.qs-text-item`);e.forEach((e,t)=>{let n=t===this.selectedTextIndex;e.classList.toggle(`selected`,n),e.setAttribute(`aria-selected`,n?`true`:`false`)}),e[this.selectedTextIndex]?.scrollIntoView({block:`nearest`}),this.setActiveDescendant(e[this.selectedTextIndex]?.id??``);return}let e=this.resultsList.querySelectorAll(`.qs-result-item`);e.forEach((e,t)=>{let n=t===this.selectedIndex;e.classList.toggle(`selected`,n),e.setAttribute(`aria-selected`,n?`true`:`false`)}),e[this.selectedIndex]?.scrollIntoView({block:`nearest`}),this.setActiveDescendant(e[this.selectedIndex]?.id??``)}setActiveDescendant(e=``){this.input?.setAttribute(`aria-activedescendant`,e)}confirmSelection(){if(this.mode===`text`){let e=this.textResultItems[this.selectedTextIndex];e&&(this.close({restoreFocus:!1}),this.onTextMatchSelect?.(e));return}let e=this.filteredFiles[this.selectedIndex];e&&(this.close({restoreFocus:!1}),this.onFileSelect?.(e))}};export{b as QuickSwitcherController};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.freeze({authError:`auth_error`,authPassword:`auth_password`,file:`file`,gitCommit:`git-commit`,gitDiff:`git-diff`,gitFileHistory:`git-file-history`,gitFilePreview:`git-file-preview`,gitHistory:`git-history`});function t(e=``){let t=String(e??``),n=t.startsWith(`#`)?t.slice(1):t;return new URLSearchParams(n)}function n(e=``){let t=String(e??``);return t.startsWith(`#`)?t.slice(1):t}function r(r=``){if(!n(r).trim().includes(`=`))return!1;let i=t(r);return Object.values(e).some(e=>i.has(e))}function i(t,{anchor:n=null,column:r=null,drawioMode:i=null,line:a=null,matchLength:o=null}={}){let s=new URLSearchParams;return t&&s.set(e.file,t),i&&s.set(`drawio`,i),n&&s.set(`anchor`,String(n).trim()),a!=null&&s.set(`line`,String(Math.max(1,Math.round(Number(a)||1)))),r!=null&&s.set(`column`,String(Math.max(1,Math.round(Number(r)||1)))),o!=null&&s.set(`matchLength`,String(Math.max(0,Math.round(Number(o)||0)))),s.toString()}function a(e){return e.endsWith(`/`)?e.slice(0,-1):e}function o(e){let t=a(String(e??``).trim());if(!t||t===`/`)return``;if(/^[a-zA-Z][a-zA-Z\d+.-]*:/u.test(t))try{return o(new URL(t).pathname)}catch{return``}return t.startsWith(`/`)?t:`/${t}`}function s(e,t){let n=a(String(e??``).trim());return!n||n===`/`?t:n.startsWith(`/`)?n:`/${n}`}function c(e,t){let n=o(e),r=String(t??``).trim();return r?/^[a-zA-Z][a-zA-Z\d+.-]*:/u.test(r)||n&&(r===n||r.startsWith(`${n}/`))?r:r===`/`?n||`/`:r.startsWith(`/`)?n?`${n}${r}`:r:n?`${n}/${r}`:`/${r}`:n||`/`}function l(){let e={auth:{enabled:!1,implemented:!0,loginEndpoint:`/api/auth/oidc/login`,provider:``,requiresLogin:!1,sessionEndpoint:`/api/auth/session`,statusEndpoint:`/api/auth/status`,strategy:`none`},basePath:``,build:{id:``,packageVersion:``},drawioBaseUrl:`https://embed.diagrams.net`,environment:`development`,gitEnabled:!0,publicWsBaseUrl:``,search:{available:!1,backend:`ripgrep`,minQueryLength:2,unavailableReason:`ripgrep search is unavailable`,version:``},structurizrEnabled:!1,wikiLinkAutoCreate:!0,wsBasePath:`/ws`,...window.__COLLABMD_CONFIG__??{}},t=o(e.basePath),n={enabled:!1,implemented:!0,loginEndpoint:`/api/auth/oidc/login`,provider:``,requiresLogin:!1,sessionEndpoint:`/api/auth/session`,statusEndpoint:`/api/auth/status`,strategy:`none`,...e.auth??{}},r={id:``,packageVersion:``,...e.build??{}};return{...e,auth:{...n,loginEndpoint:c(t,n.loginEndpoint),sessionEndpoint:c(t,n.sessionEndpoint),statusEndpoint:c(t,n.statusEndpoint)},basePath:t,build:r,wsBasePath:s(e.wsBasePath,`/ws`)}}function u(e=`/`,t=l()){return c(t.basePath,e)}function d(e=`/`,t=l()){return new URL(u(e,t),window.location.origin).toString()}function f(e=`/`,t=l()){let n=String(e??``).trim();return!n||n===`/`?u(`/api`,t):n.startsWith(`/api`)?u(n,t):u(`/api${n.startsWith(`/`)?n:`/${n}`}`,t)}function p(e=l()){if(![`development`,`test`].includes(e.environment))return``;let t=new URLSearchParams(window.location.search).get(`server`);if(!t)return``;try{let e=new URL(t,window.location.origin);return[`http:`,`https:`,`ws:`,`wss:`].includes(e.protocol)?a(e.toString()):``}catch{return``}}function m(e=l()){return p(e)||(e.publicWsBaseUrl?a(e.publicWsBaseUrl):`${window.location.protocol===`https:`?`wss:`:`ws:`}//${window.location.host}${u(e.wsBasePath,e)}`)}function h(){return l()}function g(){return t(window.location.hash)}function _(e){return e===`staged`||e===`all`||e===`working-tree`?e:`all`}function v(){let t=g();if(t.has(e.gitFilePreview)){let n=t.get(e.gitFilePreview)||null;return{currentFilePath:t.get(`current`)||n,filePath:n,hash:t.get(`hash`)||null,type:`git-file-preview`}}if(t.has(e.gitFileHistory))return{filePath:t.get(e.gitFileHistory)||null,type:`git-file-history`};if(t.has(e.gitHistory))return{type:`git-history`};if(t.has(e.gitCommit))return{hash:t.get(e.gitCommit)||null,historyFilePath:t.get(`history`)||null,path:t.get(`path`)||null,type:`git-commit`};if(t.has(e.gitDiff)){let n=t.get(e.gitDiff)||null;return{filePath:n,scope:_(t.get(`scope`)||(n?`working-tree`:`all`)),type:`git-diff`}}return t.has(e.file)?{anchor:t.get(`anchor`)||null,column:t.has(`column`)&&Number(t.get(`column`))||null,drawioMode:t.get(`drawio`)||null,filePath:t.get(e.file),line:t.has(`line`)&&Number(t.get(`line`))||null,matchLength:t.has(`matchLength`)&&Number(t.get(`matchLength`))||null,type:`file`}:{type:`empty`}}function y(e,{anchor:t=null,column:n=null,drawioMode:r=null,line:a=null,matchLength:o=null}={}){window.location.hash=i(e,{anchor:t,column:n,drawioMode:r,line:a,matchLength:o})}function b({filePath:e=null,scope:t=`all`}={}){let n=new URLSearchParams;n.set(`git-diff`,e??``),n.set(`scope`,_(t)),window.location.hash=n.toString()}function x({hash:e,path:t=null,historyFilePath:n=null}={}){let r=String(e??``).trim(),i=new URLSearchParams;i.set(`git-commit`,r),n&&i.set(`history`,n),t&&i.set(`path`,t),window.location.hash=i.toString()}function S(){let e=new URLSearchParams;e.set(`git-history`,`1`),window.location.hash=e.toString()}function C({filePath:e}={}){let t=new URLSearchParams;t.set(`git-file-history`,e??``),window.location.hash=t.toString()}function w({hash:e,path:t,currentFilePath:n=null}={}){let r=String(e??``).trim(),i=new URLSearchParams;i.set(`git-file-preview`,t??``),n&&i.set(`current`,n),i.set(`hash`,r),window.location.hash=i.toString()}export{b as a,S as c,d,m as f,r as g,t as h,x as i,f as l,i as m,h as n,C as o,p,y as r,w as s,v as t,u};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{a as e,d as t,g as n,m as r,p as i,s as a}from"./file-kind-BbqgGzpc.js";import{h as o,l as s,n as c}from"./runtime-config-BLrEKVO1.js";import{i as l,n as u}from"./browser-utils--H_DkyEF.js";var d={enabled:!1,implemented:!0,loginEndpoint:`/api/auth/oidc/login`,passwordLabel:`Password`,provider:``,requiresLogin:!1,sessionEndpoint:`/api/auth/session`,statusEndpoint:`/api/auth/status`,strategy:`none`,submitLabel:`Continue`},f=`data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAIAAAABc2X6AAAGsklEQVR4Ae3cA3jsShgG4Gvbtu1js7aObdu2bdbudmvbtu1VtUg2ydw7x95mstnt5rZ5voflG80/fzL72L89bOsF94J7wWgb2dSgiAzpPHmwbfPK1qWzxDNsRdb6Ar2BgtF9hSbDRbYG4mk2beuXwm+Q89yV+TmAILgHptrb5DyPtg3LhBZjBCP+RovewNZlc2TOl8nGel0HA4LA4qPhkRSM6XcHoEYk86fIfT2AQqFzYIBjMncHofkoZBWNCM1GSq+cpdpadQIMKEoRzIeXIroEkW08TObpDAiiO8F4Tga85TBnoEc8xVKZm9UNYECS0stnBCP/Ud+AnJH/SC+eAiShPTDZ3CRZPIN9CUokC6ZSYqE2wHhmKhw/2TegR2RnSFSUaRaMJccLxg7QsAQhktkTAACaAmOxkbA80h2taKIZKWzR1BFWRAQLRvXhphYdDEtcWDxxU4sOhjdDkdVYbmrRwYAgJIums3RTNWrftbHz/AmZh5MiLBAGVqOdZ4+1790inmrFvpYZuPPEATWdknmT5f7eZEN9V2N7o9zPu3XNInQte2BlcYE6tVTbltXw4kf9h5QlhW3rliBr1QcDAODsjPFlhmelqfNvKQty4S9B0KoPVgTxmWk7Tx0COMZCF6GzA3ZC0LWMwJSsTWiOXj+O6iP382KzowAAvMOhaxmAqw/Ijr4tGPsHgnbsACwxlpNNPABIIv5LIvxpzOVlkdXPNMGK8CCudi2pFi+ovRFlwLOtc7/uUgvnxhxu05LpIyD17nTu+EAw8q9HaVtXzgcAcBUMcAER/sx9YBj5pdeExr897NLtT9TVcLgRTzVcgbyHBuc9L5n8PdLJzAEwmWP1KDCMMvTp9jWf3tbC1gdQKDgMBhRBRL0GYaojO35zxOo8fZjbz5ZAZz700Anm9pLI9g+yoY7bYKrJBWJoRpk8SJ0/fDoc13QuRuNdgMmydfTBZPkGdcDDd0q1EDkOVIKzTOiDKVGY7oPLmylVYCKlL30wIKW6D44tJlSCE76nC45+CwHXfWDfdKVKcMwHdMEJ33MC7JqEqwRHvEgXnDaYE+ArMayBB3ECDAennnVKn4tUDU74gS446k1OgB3i8Z41LHmkKHtW4eGfpRpctv5/VlomlxOsTR6kKQN1H1wnptiZHpYFv27pMbqus4kxOChbySzTz8loakfukhIkYKEBEBzw6SBXk79cLQ5lXv5XuxtJAaMDdA/v+JMydVs8irBnDvB+g9QbGeY1SUFg2gTn1JD0z+cdPIVaTbym0JemeQ6FzrtzJtdFm+DT4Rh9MD9DybxNmxL03mg3g/u0MP3cbWo6GrWjbZMBg/0Id6wqAcWwEX+F/10fVzPIe2jmRW3VTiP+VBjC4bU6KgMAID9qaQt9frl3P6hSndOaP7HrxdSYPQiH92QYhvAwTRH7KdSWBL9h5j4GeugkqDpWc1oZBqaelSGNwMWNJMLjUkHxBn//zwZeH3topr+7bWxDmia0AID17gok7aTTMrTnw1KsfZTPVMhAyj9uVl5lIexqcQLs4KFpYXjpSjQw3HwrIqCBQQ5mXsZInBWtoIOac1GOqrU4IsUJwOSllsmha5iZTf3npzXnqXka+1dFmzo4D9vZgQp2TsAZvrZUIC7729WSmRlmVfz+HGExKpUCVGJj1oSQlTd+SZ9Lm4bubqKvtTkmk+OAIRhu+zMu3jEwyqTQ1d7lofWdzV0VyWSxpPJkjpOB76z7fsPfjjOHHMhBb0QzAhMUMS18nRrgOzHkz96YdPR4toNTMT+gKjq4OtatNPBCvgfcp9PD1w/wsFP14y7WA4/zu9SudVWw8HKpUC4Zw5uOLmQ//c4fGrZL8iit+WEpvMmxAIZbtrCor7uNLpj/ubpk6N6Kh059M6tINl8Qh4UUHGa73wzjNGHQ4dj7wC6JOPtLACLqkvq4WeuE2cWi/+nLt0csOKnQ1CKP+IYMWELqgPnGiLVh6O7Go8GYZpfxpDbnwkaHjpj3JfLRV7Wgb01SARxIupc6wntyfGOG9pbikRQJmzvodRg7mRq2tkkm7IbFlhktBdaBS7SsPZx1haDIbltOSwGKXxl5px7UZGaEb8gXlerEgmmMxB2KfEciTKHRAsvbmPo0nVsST1BEdH3Kyrh9bJVlQ7wmbk05USSu0PUPPWjDOtxLg5bF7h7tM42B08x//o7U0/AmrCSV3PtYiwZpS3B13MHMS/DIz4rYaBO0VM93JpwbwaJtqNfEsbwZFgEL50VtgcJLBV6wK9aKtfd+cEkvuBfcC37k9h8VGR+csPdltgAAAABJRU5ErkJggg==`;function p(){return{...d,...c().auth??{}}}function m(){return o(window.location.hash).get(`auth_password`)||``}function h(){let e=o(window.location.hash);if(!e.has(`auth_password`))return;e.delete(`auth_password`);let t=e.toString(),n=`${window.location.pathname}${window.location.search}${t?`#${t}`:``}`;window.history.replaceState(null,``,n)}function g(e){let t=o(window.location.hash),n=t.get(e)||``;if(!n)return``;t.delete(e);let r=t.toString(),i=`${window.location.pathname}${window.location.search}${r?`#${r}`:``}`;return window.history.replaceState(null,``,i),n}function _(){return`${window.location.pathname}${window.location.search}${window.location.hash}`}async function v(e){let t=await fetch(e.statusEndpoint,{headers:{Accept:`application/json`}}),n=await t.json().catch(()=>({}));if(!t.ok)throw Error(n.error||`Failed to read auth status`);return n}function y(e){let t=e?.auth??{};if(t.strategy!==`oidc`||t.provider!==`google`)return;let n=String(e?.user?.name??``).trim();if(n)try{window.localStorage.setItem(`collabmd-user-name`,n)}catch{}}async function b(e,t){let n=await fetch(e.sessionEndpoint,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({password:t})}),r=await n.json().catch(()=>({}));if(!n.ok)throw Error(r.error||`Authentication failed`);return r}function x(){let e=document.createElement(`div`);e.className=`auth-gate-overlay`;let t=document.createElement(`section`);return t.className=`auth-gate-card`,e.append(t),{card:t,overlay:e}}function S(){let e=document.createElement(`span`);e.className=`auth-gate-button__icon`,e.setAttribute(`aria-hidden`,`true`);let t=document.createElement(`img`);return t.alt=``,t.decoding=`async`,t.height=20,t.loading=`eager`,t.src=f,t.width=20,e.append(t),e}function C(e,t,{errorMessage:n=``}){e.replaceChildren();let r=document.createElement(`h1`);r.textContent=`Authentication required`;let i=document.createElement(`p`);i.textContent=`Sign in with Google to join this session.`;let a=document.createElement(`div`);a.className=`auth-gate-actions`;let o=document.createElement(`button`);o.className=`ui-button ui-button--secondary auth-gate-button auth-gate-button--google`,o.type=`button`,o.append(S(),document.createTextNode(t.submitLabel||`Continue with Google`));let s=document.createElement(`div`);s.className=`auth-gate-error`,s.textContent=n,a.append(o),e.append(r,i,s,a),o.addEventListener(`click`,()=>{let e=new URL(t.loginEndpoint,window.location.origin);e.searchParams.set(`returnTo`,_()),window.location.assign(e.toString())})}function w(e,{title:t,body:n,secondaryActionLabel:r=``,onSecondaryAction:i=null}){e.replaceChildren();let a=document.createElement(`h1`);a.textContent=t;let o=document.createElement(`p`);if(o.textContent=n,e.append(a,o),r&&typeof i==`function`){let t=document.createElement(`button`);t.className=`ui-button ui-button--secondary auth-gate-secondary-button`,t.type=`button`,t.textContent=r,t.addEventListener(`click`,()=>{i()}),e.append(t)}}function T(e,t,{onSubmit:n}){e.replaceChildren();let r=document.createElement(`h1`);r.textContent=`Authentication required`;let i=document.createElement(`p`);i.textContent=`Enter the host password to join this shared session.`;let a=document.createElement(`form`);a.className=`auth-gate-form`;let o=document.createElement(`label`);o.className=`auth-gate-label`,o.textContent=t.passwordLabel;let s=document.createElement(`input`);s.className=`ui-input auth-gate-input`,s.type=`password`,s.name=`password`,s.autocomplete=`current-password`,s.required=!0;let c=document.createElement(`div`);c.className=`auth-gate-error`;let l=document.createElement(`div`);l.className=`auth-gate-actions`;let u=document.createElement(`button`);u.className=`ui-button ui-button--primary auth-gate-button`,u.type=`submit`,u.textContent=t.submitLabel,l.append(u),o.append(s),a.append(r,i,o,c,l),e.append(a),a.addEventListener(`submit`,e=>{e.preventDefault();let t=s.value;s.disabled=!0,u.disabled=!0,c.textContent=``,n(t).catch(e=>{c.textContent=e instanceof Error?e.message:`Authentication failed`,s.disabled=!1,u.disabled=!1,s.select(),s.focus()})}),queueMicrotask(()=>{s.focus()})}async function E(){let e=p();if(!e.enabled||e.strategy===`none`)return{authenticated:!0,auth:e};let{card:t,overlay:n}=x();document.body.append(n);let r=g(`auth_error`);return new Promise(i=>{let a=(t=null)=>{h(),y(t),n.remove(),i({authenticated:!0,auth:t?.auth??e,user:t?.user??null})};(async()=>{w(t,{body:`Checking access to this session…`,title:`CollabMD`});try{let t=await v(e);if(t.authenticated){a(t);return}}catch(e){w(t,{body:e instanceof Error?`${e.message} Refresh the page and try again.`:`Failed to contact the auth service. Refresh the page and try again.`,title:`Cannot verify access`});return}if(e.strategy===`password`){let n=m();if(n)try{h(),await b(e,n),a();return}catch{}T(t,e,{onSubmit:async t=>{await b(e,t),a()}});return}if(e.strategy===`oidc`){C(t,e,{errorMessage:r}),r=``;return}w(t,{body:`This authentication strategy is not available.`,title:`Authentication unavailable`})})()})}function D(e){return!e||e.repeat||!e.metaKey&&!e.ctrlKey||e.shiftKey||e.altKey?!1:String(e.key??``).toLowerCase()===`k`||e.code===`KeyK`}function O(e){let t=String(e??``).trim().replace(/\\/g,`/`).replace(/^\/+/u,``).split(`/`).map(e=>e.trim()).filter(Boolean);return t.some(e=>e===`.`||e===`..`)?``:t.join(`/`)}function k(e,t){let n=O(e),r=O(t);return n?r?`${n}/${r}`:n:r}function A(e=``){return String(e??``).replace(/\/+$/u,``).split(`/`).filter(Boolean).pop()||``}function j(e=``){let t=String(e??``).replace(/\/+$/u,``),n=t.lastIndexOf(`/`);return n>=0?t.slice(0,n):``}function M(e,t){return e.toLowerCase().endsWith(t.toLowerCase())?e:`${e}${t}`}function N(e){return`# ${n(String(e??``).split(`/`).pop()||``)||`Untitled`}\n\n`}function P(t){let n=O(t),r=e(n)?n:`${n}.base`;return{content:[`views:`,` - type: table`,` name: Table`,` order:`,` - file.name`,``].join(`
|
|
2
|
+
`),path:r}}function F(e){let n=O(e),r=t(n)?n:`${n}.mmd`;return{content:[`flowchart TD`,` A[Start] --> B{Decide}`,` B -->|Yes| C[Ship it]`,` B -->|No| D[Revise]`,``].join(`
|
|
3
|
+
`),path:r}}function I(e){let t=O(e),n=a(t)?t:`${t}.drawio`;return{content:[`<mxfile host="app.diagrams.net" modified="2026-01-01T00:00:00.000Z" agent="CollabMD" version="24.7.17">`,` <diagram id="page-1" name="Page-1">`,` <mxGraphModel dx="1200" dy="800" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">`,` <root>`,` <mxCell id="0" />`,` <mxCell id="1" parent="0" />`,` </root>`,` </mxGraphModel>`,` </diagram>`,`</mxfile>`,``].join(`
|
|
4
|
+
`),path:n}}function L(e){let t=O(e),n=i(t)?t:`${t}.puml`;return{content:[`@startuml`,`Alice -> Bob: Hello`,`@enduml`,``].join(`
|
|
5
|
+
`),path:n}}function R(e){let t=O(e),n=r(t)?t:`${t}.dsl`;return{content:`workspace "CollabMD Example" "A small Structurizr workspace" {, model {, user = person "User" "Uses the system", system = softwareSystem "Example System" "The system being documented" {, web = container "Web Application" "Serves the user interface" "JavaScript" {, home = component "Home Page" "Shows the landing page" "React", editor = component "Editor" "Edits workspace content" "React", }, api = container "API" "Handles requests" "Node.js", }, user -> system "Uses", web -> api "Calls" "HTTPS/JSON", home -> editor "Links to", }, views {, systemContext system "Context" {, include *, autoLayout, }, container system "Containers" {, include *, autoLayout, }, component web "Components" {, include *, autoLayout, }, styles {, element "Person" {, shape person, }, }, },},`.split(`,`).join(`
|
|
6
|
+
`),path:n}}function z(e,t={}){let n={...t};return e&&(n[`X-CollabMD-Request-Id`]=String(e)),n}async function B(e,t){let n=await e.json().catch(()=>({}));if(!e.ok||n.ok===!1){let r=Error(n.error||t);throw r.status=e.status,r.body=n,typeof n?.code==`string`&&(r.code=n.code),r}return n}function V(e){return encodeURIComponent(String(e??``))}async function H(e,{fallbackError:t,fallbackFileName:n}={}){let r=await fetch(e);if(!r.ok){let e=await r.json().catch(()=>({}));throw Error(e.error||t)}let i=await r.blob();return u(i,l(r.headers.get(`content-disposition`)||``,n),{removeDelayMs:0,revokeDelayMs:0}),r}var U=new class{async readTree(){return B(await fetch(s(`/files`)),`Failed to load file tree`)}async readCommentOverview(){return B(await fetch(s(`/comments/overview`)),`Failed to load comment overview`)}async readFile(e){return B(await fetch(s(`/file?path=${encodeURIComponent(e)}`)),`Failed to read file`)}async readBacklinks(e,{signal:t}={}){let n=await B(await fetch(s(`/backlinks?file=${encodeURIComponent(e)}`),{signal:t}),`Failed to load backlinks`);return Array.isArray(n.backlinks)?n.backlinks:[]}async syncStructurizrWorkspace({path:e,source:t}){return B(await fetch(s(`/structurizr/sync`),{body:JSON.stringify({path:e,source:t}),headers:{"Content-Type":`application/json`},method:`POST`}),`Failed to sync Structurizr workspace`)}async renderSvg(e){let t=await B(await fetch(s(`/plantuml/render`),{body:JSON.stringify({source:e}),headers:{"Content-Type":`application/json`},method:`POST`}),`Failed to render PlantUML`);if(!t.ok||typeof t.svg!=`string`)throw Error(t.error||`Failed to render PlantUML`);return t.svg}async searchText({limit:e=50,query:t=``,signal:n=null}={}){let r=new URLSearchParams({limit:String(e),q:String(t??``)});return B(await fetch(s(`/search?${r.toString()}`),{signal:n}),`Failed to search files`)}async queryBase({activeFilePath:e=``,path:t=``,search:n=``,source:r=null,sourcePath:i=``,view:a=``}={}){return B(await fetch(s(`/base/query`),{body:JSON.stringify({activeFilePath:e,path:t,search:n,source:r,sourcePath:i,view:a}),headers:{"Content-Type":`application/json`},method:`POST`}),`Failed to query base`)}async exportBaseCsv({activeFilePath:e=``,path:t=``,search:n=``,source:r=null,sourcePath:i=``,view:a=``}={}){let o=await fetch(s(`/base/export`),{body:JSON.stringify({activeFilePath:e,path:t,search:n,source:r,sourcePath:i,view:a}),headers:{"Content-Type":`application/json`},method:`POST`});if(!o.ok){let e=await o.json().catch(()=>({}));throw Error(e.error||`Failed to export base CSV`)}return{blob:await o.blob(),contentDisposition:o.headers.get(`content-disposition`)||``}}async queryBasePropertyValues({activeFilePath:e=``,path:t=``,propertyId:n=``,query:r=``,source:i=null,sourcePath:a=``,view:o=``}={}){return B(await fetch(s(`/base/property-values`),{body:JSON.stringify({activeFilePath:e,path:t,propertyId:n,query:r,source:i,sourcePath:a,view:o}),headers:{"Content-Type":`application/json`},method:`POST`}),`Failed to load base property values`)}async transformBase({activeFilePath:e=``,mutation:t=null,path:n=``,source:r=null,sourcePath:i=``,view:a=``}={}){return B(await fetch(s(`/base/transform`),{body:JSON.stringify({activeFilePath:e,mutation:t,path:n,source:r,sourcePath:i,view:a}),headers:{"Content-Type":`application/json`},method:`POST`}),`Failed to transform base`)}async createFile({content:e,path:t,requestId:n=null}){return B(await fetch(s(`/file`),{body:JSON.stringify({content:e,path:t}),headers:z(n,{"Content-Type":`application/json`}),method:`POST`}),`Failed to create file`)}async writeFile({content:e,path:t,requestId:n=null}){return B(await fetch(s(`/file`),{body:JSON.stringify({content:e,path:t}),headers:z(n,{"Content-Type":`application/json`}),method:`PUT`}),`Failed to write file`)}async renameFile({oldPath:e,newPath:t,requestId:n=null}){return B(await fetch(s(`/file`),{body:JSON.stringify({newPath:t,oldPath:e}),headers:z(n,{"Content-Type":`application/json`}),method:`PATCH`}),`Failed to rename file`)}async deleteFile(e,{requestId:t=null}={}){return B(await fetch(s(`/file?path=${encodeURIComponent(e)}`),{headers:t?z(t):void 0,method:`DELETE`}),`Failed to delete file`)}async createDirectory(e,{requestId:t=null}={}){return B(await fetch(s(`/directory`),{body:JSON.stringify({path:e}),headers:z(t,{"Content-Type":`application/json`}),method:`POST`}),`Failed to create folder`)}async renameDirectory({oldPath:e,newPath:t,requestId:n=null}){return B(await fetch(s(`/directory`),{body:JSON.stringify({newPath:t,oldPath:e}),headers:z(n,{"Content-Type":`application/json`}),method:`PATCH`}),`Failed to rename folder`)}async deleteDirectory(e,{recursive:t=!1,requestId:n=null}={}){let r=new URLSearchParams({path:String(e??``)});return t&&r.set(`recursive`,`1`),B(await fetch(s(`/directory?${r.toString()}`),{headers:n?z(n):void 0,method:`DELETE`}),`Failed to delete folder`)}async uploadImageAttachment({file:e,fileName:t=``,sourcePath:n}){return B(await fetch(s(`/attachments`),{body:e,headers:{"Content-Type":e?.type||`application/octet-stream`,"X-CollabMD-File-Name":V(t),"X-CollabMD-Source-Path":V(n)},method:`POST`}),`Failed to upload image`)}async uploadFile({file:e,path:t,requestId:n=null}){return B(await fetch(s(`/file/upload`),{body:e,headers:z(n,{"Content-Type":e?.type||`application/octet-stream`,"X-CollabMD-File-Path":V(t)}),method:`POST`}),`Failed to upload file`)}async downloadFile(e){let t=A(e)||`download`;return H(s(`/download/file?path=${encodeURIComponent(e)}`),{fallbackError:`Failed to download file`,fallbackFileName:t})}async downloadDirectory(e){let t=A(e)||`vault`;return H(s(`/download/directory?path=${encodeURIComponent(e)}`),{fallbackError:`Failed to download folder`,fallbackFileName:`${t}.zip`})}};export{P as a,F as c,M as d,A as f,E as g,D as h,k as i,L as l,O as m,z as n,I as o,j as p,B as r,N as s,U as t,R as u};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>draw.io — CollabMD</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/drawioEditor-
|
|
7
|
+
<script type="module" crossorigin src="./assets/drawioEditor-BjiKJqlc.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/modulepreload-polyfill-P2Xu9kJm.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-HclGiUj8.js">
|
|
10
10
|
</head>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Excalidraw — CollabMD</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/excalidrawEditor-
|
|
7
|
+
<script type="module" crossorigin src="./assets/excalidrawEditor-CT2pHq7e.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/modulepreload-polyfill-P2Xu9kJm.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-HclGiUj8.js">
|
|
10
10
|
</head>
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>CollabMD Export</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/exportDocument-
|
|
7
|
+
<script type="module" crossorigin src="./assets/exportDocument-BaqcOAAW.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="./assets/modulepreload-polyfill-P2Xu9kJm.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-HclGiUj8.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="./assets/base-CxPEiHuy.js">
|
|
11
11
|
<link rel="stylesheet" crossorigin href="./assets/base-CaIEJUF9.css">
|
|
12
|
-
<link rel="stylesheet" crossorigin href="./assets/exportDocument-
|
|
12
|
+
<link rel="stylesheet" crossorigin href="./assets/exportDocument-B2L8fe34.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body data-theme="light" data-surface="export-document">
|
|
15
15
|
<main class="export-page-shell">
|
package/dist/client/index.html
CHANGED
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
document.documentElement.setAttribute('data-initial-file-requested', 'true');
|
|
16
16
|
}
|
|
17
17
|
</script>
|
|
18
|
-
<script type="module" crossorigin src="./assets/index-
|
|
18
|
+
<script type="module" crossorigin src="./assets/index-ClbjMBVt.js"></script>
|
|
19
19
|
<link rel="modulepreload" crossorigin href="./assets/modulepreload-polyfill-P2Xu9kJm.js">
|
|
20
20
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-HclGiUj8.js">
|
|
21
21
|
<link rel="modulepreload" crossorigin href="./assets/base-CxPEiHuy.js">
|
|
22
22
|
<link rel="stylesheet" crossorigin href="./assets/base-CaIEJUF9.css">
|
|
23
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
23
|
+
<link rel="stylesheet" crossorigin href="./assets/index-Pum8ET-5.css">
|
|
24
24
|
</head>
|
|
25
25
|
<body>
|
|
26
26
|
|
|
@@ -363,6 +363,18 @@
|
|
|
363
363
|
<span class="ui-toolbar-button-label">Search files</span>
|
|
364
364
|
</button>
|
|
365
365
|
|
|
366
|
+
<button class="ui-button ui-button--ghost ui-button--toolbar ui-button--toggle toolbar-vim-button" id="toggleVimModeBtn" aria-label="Enable Vim keybindings" aria-pressed="false" title="Enable Vim keybindings">
|
|
367
|
+
<span class="toolbar-vim-icon" aria-hidden="true">
|
|
368
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
|
369
|
+
<rect x="3" y="4" width="18" height="16" rx="2"></rect>
|
|
370
|
+
<path d="m7 9 3 3-3 3"></path>
|
|
371
|
+
<path d="M13 15h4"></path>
|
|
372
|
+
</svg>
|
|
373
|
+
</span>
|
|
374
|
+
<span class="ui-toolbar-button-label">Vim</span>
|
|
375
|
+
<span class="ui-pill-badge ui-pill-badge--muted toolbar-vim-state" id="vimModeToggleLabel">Off</span>
|
|
376
|
+
</button>
|
|
377
|
+
|
|
366
378
|
<details class="toolbar-overflow-group hidden" id="exportMenuGroup">
|
|
367
379
|
<summary class="ui-button ui-button--ghost ui-button--toolbar toolbar-overflow-group-toggle" aria-label="Export options" title="Export">
|
|
368
380
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
@@ -734,7 +746,7 @@
|
|
|
734
746
|
<input type="text" class="qs-input" id="quickSwitcherInput" placeholder="Search files..." aria-label="Search files" aria-controls="quickSwitcherResults" aria-describedby="quickSwitcherHint quickSwitcherScope" autocomplete="off" spellcheck="false">
|
|
735
747
|
<kbd class="qs-kbd">esc</kbd>
|
|
736
748
|
</div>
|
|
737
|
-
<div class="qs-scope hidden" id="quickSwitcherScope" role="note" aria-hidden="true">Exact, case-insensitive text in Markdown, Base, Mermaid, PlantUML,
|
|
749
|
+
<div class="qs-scope hidden" id="quickSwitcherScope" role="note" aria-hidden="true">Exact, case-insensitive text in Markdown, Base, Mermaid, PlantUML, Draw.io, and Excalidraw files. Images are excluded.</div>
|
|
738
750
|
<div class="qs-results" id="quickSwitcherResults" role="listbox" aria-label="Search results" aria-busy="false" tabindex="-1"></div>
|
|
739
751
|
<div class="qs-hint" id="quickSwitcherHint" role="status" aria-live="polite" aria-atomic="true">Type to search files</div>
|
|
740
752
|
<div class="qs-footer">
|
package/docker-compose.yml
CHANGED
|
@@ -3,6 +3,7 @@ services:
|
|
|
3
3
|
image: ${COLLABMD_IMAGE:-ghcr.io/andes90/collabmd:latest}
|
|
4
4
|
depends_on:
|
|
5
5
|
- plantuml
|
|
6
|
+
- structurizr
|
|
6
7
|
environment:
|
|
7
8
|
HOST: 0.0.0.0
|
|
8
9
|
PORT: 1234
|
|
@@ -19,7 +20,11 @@ services:
|
|
|
19
20
|
BASE_PATH: ${BASE_PATH:-}
|
|
20
21
|
PUBLIC_WS_BASE_URL: ${PUBLIC_WS_BASE_URL:-}
|
|
21
22
|
PLANTUML_SERVER_URL: http://plantuml:8080
|
|
23
|
+
STRUCTURIZR_SERVER_URL: http://structurizr:8080
|
|
24
|
+
STRUCTURIZR_MIRROR_DIR: ${COLLABMD_VAULT_DIR:-/data}/.collabmd/structurizr
|
|
25
|
+
STRUCTURIZR_TRUSTED_EXECUTABLE_DSL: ${STRUCTURIZR_TRUSTED_EXECUTABLE_DSL:-false}
|
|
22
26
|
COLLABMD_WIKI_LINK_AUTO_CREATE: ${COLLABMD_WIKI_LINK_AUTO_CREATE:-true}
|
|
27
|
+
COLLABMD_MAX_PDF_UPLOAD_BYTES: ${COLLABMD_MAX_PDF_UPLOAD_BYTES:-52428800}
|
|
23
28
|
COLLABMD_VAULT_DIR: ${COLLABMD_VAULT_DIR:-/data}
|
|
24
29
|
COLLABMD_GIT_REPO_URL: ${COLLABMD_GIT_REPO_URL:-}
|
|
25
30
|
COLLABMD_GIT_SSH_PRIVATE_KEY_FILE: ${COLLABMD_GIT_SSH_PRIVATE_KEY_FILE:-}
|
|
@@ -36,6 +41,9 @@ services:
|
|
|
36
41
|
ports:
|
|
37
42
|
- "${COLLABMD_HOST_PORT:-1234}:1234"
|
|
38
43
|
restart: unless-stopped
|
|
44
|
+
networks:
|
|
45
|
+
- default
|
|
46
|
+
- diagram-private
|
|
39
47
|
volumes:
|
|
40
48
|
- type: bind
|
|
41
49
|
source: ${HOST_VAULT_DIR:-./data/vault}
|
|
@@ -52,6 +60,40 @@ services:
|
|
|
52
60
|
|
|
53
61
|
plantuml:
|
|
54
62
|
image: plantuml/plantuml-server:jetty
|
|
63
|
+
networks:
|
|
64
|
+
- diagram-private
|
|
55
65
|
ports:
|
|
56
66
|
- "127.0.0.1:${PLANTUML_HOST_PORT:-18080}:8080"
|
|
57
67
|
restart: unless-stopped
|
|
68
|
+
|
|
69
|
+
structurizr:
|
|
70
|
+
image: structurizr/structurizr:2026.06.28
|
|
71
|
+
command: ["local", "/data"]
|
|
72
|
+
environment:
|
|
73
|
+
STRUCTURIZR_EDITABLE: "false"
|
|
74
|
+
STRUCTURIZR_AUTOREFRESHINTERVAL: "0"
|
|
75
|
+
STRUCTURIZR_AUTOSAVEINTERVAL: "0"
|
|
76
|
+
STRUCTURIZR_NETWORK_URLS_ALLOWED: ${STRUCTURIZR_NETWORK_URLS_ALLOWED:-}
|
|
77
|
+
networks:
|
|
78
|
+
- diagram-private
|
|
79
|
+
security_opt:
|
|
80
|
+
- no-new-privileges:true
|
|
81
|
+
cap_drop:
|
|
82
|
+
- ALL
|
|
83
|
+
cpus: 1.0
|
|
84
|
+
mem_limit: 768m
|
|
85
|
+
pids_limit: 256
|
|
86
|
+
read_only: true
|
|
87
|
+
restart: unless-stopped
|
|
88
|
+
ports:
|
|
89
|
+
- "127.0.0.1:${STRUCTURIZR_HOST_PORT:-19090}:8080"
|
|
90
|
+
tmpfs:
|
|
91
|
+
- /tmp
|
|
92
|
+
volumes:
|
|
93
|
+
- type: bind
|
|
94
|
+
source: ${HOST_VAULT_DIR:-./data/vault}/.collabmd/structurizr
|
|
95
|
+
target: /data
|
|
96
|
+
|
|
97
|
+
networks:
|
|
98
|
+
diagram-private:
|
|
99
|
+
driver: bridge
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "collabmd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.46",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Collaborative markdown vault
|
|
5
|
+
"description": "Collaborative markdown vault \u2014 like Obsidian, but online. Serve any directory of markdown files for realtime collaborative editing.",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/andes90/collabmd.git"
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"docker-compose.yml",
|
|
19
19
|
"scripts/cloudflare-tunnel.mjs",
|
|
20
20
|
"scripts/local-plantuml-compose.mjs",
|
|
21
|
+
"scripts/local-structurizr-compose.mjs",
|
|
21
22
|
"src/"
|
|
22
23
|
],
|
|
23
24
|
"bin": {
|
|
@@ -34,8 +35,11 @@
|
|
|
34
35
|
"lint": "eslint .",
|
|
35
36
|
"plantuml:up": "node scripts/local-plantuml-compose.mjs up",
|
|
36
37
|
"plantuml:down": "node scripts/local-plantuml-compose.mjs down",
|
|
38
|
+
"structurizr:up": "node scripts/local-structurizr-compose.mjs up",
|
|
39
|
+
"structurizr:down": "node scripts/local-structurizr-compose.mjs down",
|
|
37
40
|
"start": "npm run build && node bin/collabmd.js",
|
|
38
41
|
"start:local-plantuml": "npm run build && node bin/collabmd.js --local-plantuml",
|
|
42
|
+
"start:local-structurizr": "npm run build && node bin/collabmd.js --local-structurizr",
|
|
39
43
|
"start:prod": "node bin/collabmd.js",
|
|
40
44
|
"test": "npm run test:guardrails && npm run test:unit && npm run test:integration:prebuilt && npm run test:browser && npm run test:e2e:prebuilt",
|
|
41
45
|
"test:browser": "vitest run --config vitest.config.mjs --browser=chromium",
|
|
@@ -74,6 +78,7 @@
|
|
|
74
78
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
75
79
|
"@codemirror/view": "^6.43.8",
|
|
76
80
|
"@excalidraw/excalidraw": "0.18.0-4872083",
|
|
81
|
+
"@replit/codemirror-vim": "^6.4.0",
|
|
77
82
|
"@turbodocx/html-to-docx": "^1.22.0",
|
|
78
83
|
"archiver": "^8.0.0",
|
|
79
84
|
"highlight.js": "^11.11.1",
|
|
@@ -82,6 +87,7 @@
|
|
|
82
87
|
"markdown-it": "^15.0.0",
|
|
83
88
|
"mermaid": "^11.16.1",
|
|
84
89
|
"openid-client": "^6.8.4",
|
|
90
|
+
"pdfjs-dist": "6.2.108",
|
|
85
91
|
"react": "^19.2.8",
|
|
86
92
|
"react-dom": "^19.2.8",
|
|
87
93
|
"sharp": "^0.35.3",
|
|
@@ -93,8 +99,15 @@
|
|
|
93
99
|
},
|
|
94
100
|
"overrides": {
|
|
95
101
|
"@excalidraw/excalidraw": {
|
|
96
|
-
"@radix-ui/react-tabs": "1.1.13"
|
|
97
|
-
|
|
102
|
+
"@radix-ui/react-tabs": "1.1.13",
|
|
103
|
+
"nanoid": "3.3.18"
|
|
104
|
+
},
|
|
105
|
+
"@excalidraw/mermaid-to-excalidraw": {
|
|
106
|
+
"nanoid": "5.1.16"
|
|
107
|
+
},
|
|
108
|
+
"lodash-es": "4.18.1",
|
|
109
|
+
"nanoid": "3.3.18",
|
|
110
|
+
"axios": "1.18.0"
|
|
98
111
|
},
|
|
99
112
|
"devDependencies": {
|
|
100
113
|
"@eslint/js": "^10.0.1",
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { mkdir } from 'node:fs/promises';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const scriptPath = fileURLToPath(import.meta.url);
|
|
7
|
+
const scriptDir = dirname(scriptPath);
|
|
8
|
+
const projectRoot = resolve(scriptDir, '..');
|
|
9
|
+
const composeFile = resolve(projectRoot, 'docker-compose.yml');
|
|
10
|
+
|
|
11
|
+
function runCommand(command, args, { env = process.env } = {}) {
|
|
12
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
13
|
+
const child = spawn(command, args, {
|
|
14
|
+
cwd: projectRoot,
|
|
15
|
+
env,
|
|
16
|
+
stdio: 'inherit',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
child.on('error', rejectPromise);
|
|
20
|
+
child.on('exit', (code) => {
|
|
21
|
+
if (code === 0) {
|
|
22
|
+
resolvePromise();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
rejectPromise(new Error(`${command} ${args.join(' ')} exited with code ${code}`));
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getLocalStructurizrHostPort() {
|
|
32
|
+
const rawValue = process.env.STRUCTURIZR_HOST_PORT || '19090';
|
|
33
|
+
const parsed = Number.parseInt(rawValue, 10);
|
|
34
|
+
|
|
35
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
36
|
+
throw new Error(`Invalid STRUCTURIZR_HOST_PORT: ${rawValue}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return parsed;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getLocalStructurizrServerUrl() {
|
|
43
|
+
return `http://127.0.0.1:${getLocalStructurizrHostPort()}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getComposeArgs(command) {
|
|
47
|
+
return ['compose', '-f', composeFile, ...command];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function startLocalStructurizrComposeService({ vaultDir = process.env.COLLABMD_VAULT_DIR || '.' } = {}) {
|
|
51
|
+
const hostVaultDir = resolve(vaultDir);
|
|
52
|
+
await mkdir(resolve(hostVaultDir, '.collabmd/structurizr'), { recursive: true });
|
|
53
|
+
await runCommand('docker', getComposeArgs(['up', '-d', 'structurizr']), {
|
|
54
|
+
env: {
|
|
55
|
+
...process.env,
|
|
56
|
+
HOST_VAULT_DIR: hostVaultDir,
|
|
57
|
+
STRUCTURIZR_HOST_PORT: String(getLocalStructurizrHostPort()),
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
return getLocalStructurizrServerUrl();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function stopLocalStructurizrComposeService() {
|
|
64
|
+
await runCommand('docker', getComposeArgs(['stop', 'structurizr']));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function main() {
|
|
68
|
+
const command = process.argv[2] || 'up';
|
|
69
|
+
|
|
70
|
+
if (command === 'up') {
|
|
71
|
+
const url = await startLocalStructurizrComposeService();
|
|
72
|
+
console.log(`[structurizr] Local Structurizr renderer is available at ${url}`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (command === 'down') {
|
|
77
|
+
await stopLocalStructurizrComposeService();
|
|
78
|
+
console.log('[structurizr] Local Structurizr renderer stopped');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (command === 'url') {
|
|
83
|
+
console.log(getLocalStructurizrServerUrl());
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.error('Usage: node scripts/local-structurizr-compose.mjs [up|down|url]');
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (process.argv[1] && resolve(process.argv[1]) === scriptPath) {
|
|
92
|
+
main().catch((error) => {
|
|
93
|
+
if (error.code === 'ENOENT') {
|
|
94
|
+
console.error('[structurizr] Docker is not available. Install Docker Desktop or Docker Engine first.');
|
|
95
|
+
} else {
|
|
96
|
+
console.error(`[structurizr] ${error.message}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
process.exit(1);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -357,6 +357,18 @@
|
|
|
357
357
|
<span class="ui-toolbar-button-label">Search files</span>
|
|
358
358
|
</button>
|
|
359
359
|
|
|
360
|
+
<button class="ui-button ui-button--ghost ui-button--toolbar ui-button--toggle toolbar-vim-button" id="toggleVimModeBtn" aria-label="Enable Vim keybindings" aria-pressed="false" title="Enable Vim keybindings">
|
|
361
|
+
<span class="toolbar-vim-icon" aria-hidden="true">
|
|
362
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
|
363
|
+
<rect x="3" y="4" width="18" height="16" rx="2"></rect>
|
|
364
|
+
<path d="m7 9 3 3-3 3"></path>
|
|
365
|
+
<path d="M13 15h4"></path>
|
|
366
|
+
</svg>
|
|
367
|
+
</span>
|
|
368
|
+
<span class="ui-toolbar-button-label">Vim</span>
|
|
369
|
+
<span class="ui-pill-badge ui-pill-badge--muted toolbar-vim-state" id="vimModeToggleLabel">Off</span>
|
|
370
|
+
</button>
|
|
371
|
+
|
|
360
372
|
<details class="toolbar-overflow-group hidden" id="exportMenuGroup">
|
|
361
373
|
<summary class="ui-button ui-button--ghost ui-button--toolbar toolbar-overflow-group-toggle" aria-label="Export options" title="Export">
|
|
362
374
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
@@ -728,7 +740,7 @@
|
|
|
728
740
|
<input type="text" class="qs-input" id="quickSwitcherInput" placeholder="Search files..." aria-label="Search files" aria-controls="quickSwitcherResults" aria-describedby="quickSwitcherHint quickSwitcherScope" autocomplete="off" spellcheck="false">
|
|
729
741
|
<kbd class="qs-kbd">esc</kbd>
|
|
730
742
|
</div>
|
|
731
|
-
<div class="qs-scope hidden" id="quickSwitcherScope" role="note" aria-hidden="true">Exact, case-insensitive text in Markdown, Base, Mermaid, PlantUML,
|
|
743
|
+
<div class="qs-scope hidden" id="quickSwitcherScope" role="note" aria-hidden="true">Exact, case-insensitive text in Markdown, Base, Mermaid, PlantUML, Draw.io, and Excalidraw files. Images are excluded.</div>
|
|
732
744
|
<div class="qs-results" id="quickSwitcherResults" role="listbox" aria-label="Search results" aria-busy="false" tabindex="-1"></div>
|
|
733
745
|
<div class="qs-hint" id="quickSwitcherHint" role="status" aria-live="polite" aria-atomic="true">Type to search files</div>
|
|
734
746
|
<div class="qs-footer">
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
getVaultFileKind,
|
|
3
|
+
supportsCommentsForFilePath,
|
|
4
|
+
} from '../../../domain/file-kind.js';
|
|
2
5
|
|
|
3
6
|
export const commentsFeature = {
|
|
4
7
|
createCommentThread(payload) {
|
|
@@ -82,19 +85,20 @@ export const commentsFeature = {
|
|
|
82
85
|
return;
|
|
83
86
|
}
|
|
84
87
|
|
|
88
|
+
const isDiagram = getVaultFileKind(filePath) === 'excalidraw';
|
|
85
89
|
const line = Number.isFinite(anchor?.startLine) ? anchor.startLine : null;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
if (!isDiagram) {
|
|
91
|
+
this._pendingCommentOverviewFocus = {
|
|
92
|
+
attempts: 0,
|
|
93
|
+
filePath,
|
|
94
|
+
line,
|
|
95
|
+
threadId,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
92
98
|
this.setSidebarTab('comments');
|
|
93
99
|
this.setSidebarVisibility(true);
|
|
94
100
|
this.workspaceRouteController?.preserveSidebarTabForNextFileRoute?.(filePath);
|
|
95
|
-
this.navigation.navigateToFile(filePath, {
|
|
96
|
-
line,
|
|
97
|
-
});
|
|
101
|
+
this.navigation.navigateToFile(filePath, isDiagram ? undefined : { line });
|
|
98
102
|
|
|
99
103
|
if (this.currentFilePath !== filePath || !this.session) {
|
|
100
104
|
await this.workspaceRouteController.openFile(filePath);
|
|
@@ -102,6 +106,11 @@ export const commentsFeature = {
|
|
|
102
106
|
|
|
103
107
|
this.setSidebarTab('comments');
|
|
104
108
|
this.setSidebarVisibility(true);
|
|
109
|
+
if (isDiagram) {
|
|
110
|
+
this.excalidrawEmbed?.openCommentThread?.(filePath, threadId);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
105
114
|
this.focusPendingCommentOverviewThread();
|
|
106
115
|
},
|
|
107
116
|
|
|
@@ -446,6 +446,18 @@ export const gitFeature = {
|
|
|
446
446
|
showToast: true,
|
|
447
447
|
});
|
|
448
448
|
|
|
449
|
+
const structurizrDependencyChanged = [
|
|
450
|
+
...workspaceChange.changedPaths,
|
|
451
|
+
...workspaceChange.deletedPaths,
|
|
452
|
+
...workspaceChange.renamedPaths.flatMap((entry) => [entry.oldPath, entry.newPath]),
|
|
453
|
+
].some((pathValue) => /(?:\.dsl$|(?:^|\/)workspace\.json$)/iu.test(pathValue));
|
|
454
|
+
if (this.currentFilePath && this.isStructurizrWorkspaceFile?.(this.currentFilePath) && structurizrDependencyChanged) {
|
|
455
|
+
this.structurizrPreview?.queueSync({
|
|
456
|
+
filePath: this.currentFilePath,
|
|
457
|
+
source: this.session?.getText?.() ?? '',
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
|
|
449
461
|
if (
|
|
450
462
|
event.origin === 'filesystem'
|
|
451
463
|
&& this.currentFilePath
|
|
@@ -13,8 +13,9 @@ function getConnectedPresenceState(context) {
|
|
|
13
13
|
const isDrawioRoute = Boolean(context.currentFilePath && context.isDrawioFile?.(context.currentFilePath));
|
|
14
14
|
const isExcalidrawRoute = Boolean(context.currentFilePath && context.isExcalidrawFile?.(context.currentFilePath));
|
|
15
15
|
const isImageRoute = Boolean(context.currentFilePath && context.isImageFile?.(context.currentFilePath));
|
|
16
|
+
const isPdfRoute = Boolean(context.currentFilePath && context.isPdfFile?.(context.currentFilePath));
|
|
16
17
|
const shouldUseLobbyState = !hasEditorSession
|
|
17
|
-
&& (!context.currentFilePath || isDrawioRoute || isExcalidrawRoute || isImageRoute);
|
|
18
|
+
&& (!context.currentFilePath || isDrawioRoute || isExcalidrawRoute || isImageRoute || isPdfRoute);
|
|
18
19
|
return shouldUseLobbyState
|
|
19
20
|
? context.lobby?.getConnectionState?.() ?? context.connectionState
|
|
20
21
|
: context.connectionState;
|
|
@@ -62,6 +62,7 @@ function initialize() {
|
|
|
62
62
|
this.syncIdentityManagementUi();
|
|
63
63
|
this.syncCurrentUserName();
|
|
64
64
|
this.syncWrapToggle();
|
|
65
|
+
this.syncVimModeToggle();
|
|
65
66
|
this.syncToolbarOverflowVisibility?.();
|
|
66
67
|
this.syncFileHistoryButton({ mode: 'empty' });
|
|
67
68
|
this.renderChat();
|
|
@@ -297,6 +298,10 @@ function bindEvents() {
|
|
|
297
298
|
this.toggleLineWrapping();
|
|
298
299
|
});
|
|
299
300
|
|
|
301
|
+
this.elements.toggleVimModeButton?.addEventListener('click', () => {
|
|
302
|
+
this.toggleVimMode();
|
|
303
|
+
});
|
|
304
|
+
|
|
300
305
|
this.elements.userCount?.addEventListener('click', (event) => {
|
|
301
306
|
event.preventDefault();
|
|
302
307
|
this.togglePresencePanel?.();
|
|
@@ -313,7 +318,8 @@ function bindEvents() {
|
|
|
313
318
|
});
|
|
314
319
|
|
|
315
320
|
this.elements.toolbarOverflowMenu?.addEventListener('click', (event) => {
|
|
316
|
-
|
|
321
|
+
const button = event.target instanceof Element ? event.target.closest('button') : null;
|
|
322
|
+
if (button && button !== this.elements.toggleVimModeButton) {
|
|
317
323
|
this.closeToolbarOverflowMenu();
|
|
318
324
|
}
|
|
319
325
|
});
|
|
@@ -731,7 +737,12 @@ function toggleToolbarOverflowMenu() {
|
|
|
731
737
|
/** @this {UiShellContext} */
|
|
732
738
|
function handleThemeChange(theme) {
|
|
733
739
|
this.previewRenderer.applyTheme(theme);
|
|
734
|
-
if (
|
|
740
|
+
if (
|
|
741
|
+
!this.isExcalidrawFile(this.currentFilePath)
|
|
742
|
+
&& !this.isImageFile?.(this.currentFilePath)
|
|
743
|
+
&& !this.isPdfFile?.(this.currentFilePath)
|
|
744
|
+
&& !this.isStructurizrWorkspaceFile?.(this.currentFilePath)
|
|
745
|
+
) {
|
|
735
746
|
this.previewRenderer.queueRender();
|
|
736
747
|
}
|
|
737
748
|
this.session?.applyTheme(theme);
|
|
@@ -773,6 +784,35 @@ function getStoredLineWrapping() {
|
|
|
773
784
|
return this.preferences.getLineWrappingEnabled();
|
|
774
785
|
}
|
|
775
786
|
|
|
787
|
+
/** @this {UiShellContext} */
|
|
788
|
+
function toggleVimMode() {
|
|
789
|
+
const currentState = this.session?.isVimModeEnabled?.() ?? this.getStoredVimMode();
|
|
790
|
+
const nextState = !currentState;
|
|
791
|
+
|
|
792
|
+
this.session?.setVimMode?.(nextState);
|
|
793
|
+
this.preferences.setVimModeEnabled(nextState);
|
|
794
|
+
this.syncVimModeToggle(nextState);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/** @this {UiShellContext} */
|
|
798
|
+
function getStoredVimMode() {
|
|
799
|
+
return this.preferences.getVimModeEnabled();
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/** @this {UiShellContext} */
|
|
803
|
+
function syncVimModeToggle(state) {
|
|
804
|
+
const enabled = state ?? this.session?.isVimModeEnabled?.() ?? this.getStoredVimMode();
|
|
805
|
+
const label = this.elements.vimModeToggleLabel;
|
|
806
|
+
const button = this.elements.toggleVimModeButton;
|
|
807
|
+
const nextLabel = enabled ? 'On' : 'Off';
|
|
808
|
+
if (label) label.textContent = nextLabel;
|
|
809
|
+
if (button) {
|
|
810
|
+
button.setAttribute('aria-label', `${enabled ? 'Disable' : 'Enable'} Vim keybindings`);
|
|
811
|
+
button.setAttribute('title', `${enabled ? 'Disable' : 'Enable'} Vim keybindings`);
|
|
812
|
+
button.setAttribute('aria-pressed', String(enabled));
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
776
816
|
/** @this {UiShellContext} */
|
|
777
817
|
function syncWrapToggle(state) {
|
|
778
818
|
const enabled = state ?? this.session?.isLineWrappingEnabled() ?? this.getStoredLineWrapping();
|
|
@@ -836,6 +876,7 @@ export const uiFeatureShellMethods = {
|
|
|
836
876
|
copyPreviewHeadingLink,
|
|
837
877
|
createPreviewHeadingLinkUrl,
|
|
838
878
|
getStoredLineWrapping,
|
|
879
|
+
getStoredVimMode,
|
|
839
880
|
handleConnectionChange,
|
|
840
881
|
handleDocumentKeydown,
|
|
841
882
|
handleDocumentPointerDown,
|
|
@@ -856,7 +897,9 @@ export const uiFeatureShellMethods = {
|
|
|
856
897
|
syncPreviewHeadingLinkButtons,
|
|
857
898
|
syncVisualViewportBounds,
|
|
858
899
|
syncToolbarOverflowVisibility,
|
|
900
|
+
syncVimModeToggle,
|
|
859
901
|
syncWrapToggle,
|
|
860
902
|
toggleToolbarOverflowMenu,
|
|
861
903
|
toggleLineWrapping,
|
|
904
|
+
toggleVimMode,
|
|
862
905
|
};
|