diff --git a/resources/css/common.css b/resources/css/common.css index 02375ad95..9193de449 100644 --- a/resources/css/common.css +++ b/resources/css/common.css @@ -789,3 +789,12 @@ a { font-weight: 600; font-size: 13px; } + +/* excalidraw */ +.Island > div > div > div { + width: 44px; +} + +.excalidraw hr { + margin: 0; +} diff --git a/resources/css/excalidraw.min.css b/resources/css/excalidraw.min.css index 190340b0d..b957d0fc3 100644 --- a/resources/css/excalidraw.min.css +++ b/resources/css/excalidraw.min.css @@ -1,40 +1,2 @@ -.popover{position:absolute;z-index:10}.popover .cover{position:fixed;top:0;left:0;right:0;bottom:0} - -.context-menu{position:relative;border-radius:4px;box-shadow:0px 3px 10px rgba(0,0,0,0.2);padding:0;list-style:none;user-select:none;margin:-0.25rem 0 0 0.125rem;padding:0.25rem 0;background-color:#f1f3f5;border:1px solid #adb5bd}.context-menu-option{position:relative;width:100%;min-width:9.5rem;margin:0;padding:0.25rem 1rem 0.25rem 1.25rem;text-align:start;border-radius:0;background-color:transparent;border:none;white-space:nowrap}.context-menu-option:hover{color:#fff;background-color:#339af0}.context-menu-option:focus{z-index:1} - -:root{--button-gray-1: #e9ecef;--button-gray-2: #ced4da;--button-gray-3: #adb5bd;--button-blue: #a5d8ff}.ToolIcon{display:inline-flex;align-items:center;position:relative;font-family:Cascadia;cursor:pointer;background-color:var(--button-gray-1);-webkit-tap-highlight-color:transparent;border-radius:var(--space-factor)}.ToolIcon__icon{width:2.5rem;height:2.5rem;display:flex;justify-content:center;align-items:center;border-radius:var(--space-factor)}.ToolIcon__icon svg{position:relative;height:1em}.ToolIcon__icon+.ToolIcon__label{margin-inline-start:0}.ToolIcon__label{font-family:var(--ui-font);margin:0 0.8em;text-overflow:ellipsis}.ToolIcon_size_s .ToolIcon__icon{width:1.4rem;height:1.4rem;font-size:0.8em}.excalidraw .ToolIcon_type_button,.Modal .ToolIcon_type_button,.ToolIcon_type_button{padding:0;border:none;margin:0;font-size:inherit}.excalidraw .ToolIcon_type_button:hover,.Modal .ToolIcon_type_button:hover,.ToolIcon_type_button:hover{background-color:var(--button-gray-1)}.excalidraw .ToolIcon_type_button:active,.Modal .ToolIcon_type_button:active,.ToolIcon_type_button:active{background-color:var(--button-gray-2)}.excalidraw .ToolIcon_type_button:focus,.Modal .ToolIcon_type_button:focus,.ToolIcon_type_button:focus{box-shadow:0 0 0 2px var(--button-blue)}.excalidraw .ToolIcon_type_button.ToolIcon--selected,.Modal .ToolIcon_type_button.ToolIcon--selected,.ToolIcon_type_button.ToolIcon--selected{background-color:var(--button-gray-2)}.excalidraw .ToolIcon_type_button.ToolIcon--selected:active,.Modal .ToolIcon_type_button.ToolIcon--selected:active,.ToolIcon_type_button.ToolIcon--selected:active{background-color:var(--button-gray-3)}.excalidraw .ToolIcon_type_button--show,.Modal .ToolIcon_type_button--show,.ToolIcon_type_button--show{visibility:visible}.excalidraw .ToolIcon_type_button--hide,.Modal .ToolIcon_type_button--hide,.ToolIcon_type_button--hide{visibility:hidden}.ToolIcon_type_radio,.ToolIcon_type_checkbox{position:absolute;opacity:0;pointer-events:none}.ToolIcon_type_radio:checked+.ToolIcon__icon,.ToolIcon_type_checkbox:checked+.ToolIcon__icon{background-color:var(--button-gray-2)}.ToolIcon_type_radio:focus+.ToolIcon__icon,.ToolIcon_type_checkbox:focus+.ToolIcon__icon{box-shadow:0 0 0 2px var(--button-blue)}.ToolIcon_type_radio:active+.ToolIcon__icon,.ToolIcon_type_checkbox:active+.ToolIcon__icon{background-color:var(--button-gray-3)}.ToolIcon_type_floating{background-color:transparent}.ToolIcon_type_floating:hover{background-color:transparent}.ToolIcon_type_floating:active{background-color:transparent}.ToolIcon_type_floating:focus{box-shadow:none}.ToolIcon_type_floating .ToolIcon__icon{width:2rem;height:2em}.ToolIcon.ToolIcon__lock.ToolIcon_type_floating{margin-left:0.1rem}.ToolIcon__keybinding{position:absolute;bottom:2px;right:3px;font-size:0.5em;color:var(--button-gray-3);font-family:var(--ui-font);user-select:none}@media (max-width: 360px){.ToolIcon.ToolIcon__lock{display:inline-block;position:absolute;top:60px;right:-8px;margin-left:0;border-radius:20px 0 0 20px;background-color:var(--button-gray-1)}.ToolIcon.ToolIcon__lock:hover{background-color:var(--button-gray-1)}.ToolIcon.ToolIcon__lock:active{background-color:var(--button-gray-2)}.ToolIcon.ToolIcon__lock .ToolIcon__icon{width:2.5rem;height:2.5rem;border-radius:inherit}.ToolIcon.ToolIcon__lock svg{position:static}}:root[dir="ltr"] .unlocked-icon{left:2px}:root[dir="rtl"] .unlocked-icon{right:2px} - -.color-picker{background:#fff;border:0px solid rgba(255,255,255,0.25);box-shadow:rgba(0,0,0,0.25) 0px 1px 4px;border-radius:4px;position:absolute}:root[dir="ltr"] .color-picker{left:-5.5px}:root[dir="rtl"] .color-picker{right:-5.5px}.color-picker-control-container{display:grid;grid-template-columns:auto 1fr;align-items:center}.color-picker-triangle{width:0px;height:0px;border-style:solid;border-width:0px 9px 10px;border-color:transparent transparent #fff;position:absolute;top:-10px}:root[dir="ltr"] .color-picker-triangle{left:12px}:root[dir="rtl"] .color-picker-triangle{right:12px}.color-picker-triangle-shadow{border-color:transparent transparent rgba(0,0,0,0.1);top:-11px}.color-picker-content{padding:0.5rem;display:grid;grid-template-columns:repeat(5, auto);grid-gap:0.5rem}.color-picker-content .color-input-container{grid-column:1 / span 5}.excalidraw .color-picker-swatch{position:relative;height:1.875rem;width:1.875rem;cursor:pointer;border-radius:4px;margin:0;box-sizing:border-box;border:1px solid #ddd;background-color:currentColor !important}.excalidraw .color-picker-swatch:focus{box-shadow:0 0 4px 1px currentColor;border-color:#339af0}.color-picker-transparent{border-radius:4px;box-shadow:rgba(0,0,0,0.1) 0px 0px 0px 1px inset;position:absolute;top:0px;right:0px;bottom:0px;left:0px}.color-picker-transparent,.color-picker-label-swatch{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMUlEQVQ4T2NkYGAQYcAP3uCTZhw1gGGYhAGBZIA/nYDCgBDAm9BGDWAAJyRCgLaBCAAgXwixzAS0pgAAAABJRU5ErkJggg==") left center}.color-picker-hash{background:#dee2e6;height:1.875rem;width:1.875rem;color:#495057;display:flex;align-items:center;justify-content:center;position:relative}:root[dir="ltr"] .color-picker-hash{border-radius:4px 0px 0px 4px}:root[dir="rtl"] .color-picker-hash{border-radius:0px 4px 4px 0px}.color-input-container:focus-within .color-picker-hash{box-shadow:0 0 0 2px #a5d8ff}.color-input-container:focus-within .color-picker-hash::before,.color-input-container:focus-within .color-picker-hash::after{content:"";width:1px;height:100%;position:absolute;top:0}.color-input-container:focus-within .color-picker-hash::before{background:#dee2e6}:root[dir="ltr"] .color-input-container:focus-within .color-picker-hash::before{right:-1px}:root[dir="rtl"] .color-input-container:focus-within .color-picker-hash::before{left:-1px}.color-input-container:focus-within .color-picker-hash::after{background:#fff}:root[dir="ltr"] .color-input-container:focus-within .color-picker-hash::after{right:-2px}:root[dir="rtl"] .color-input-container:focus-within .color-picker-hash::after{left:-2px}.color-input-container{display:flex}.color-picker-input{width:12ch;margin:0;font-size:1rem;color:#343a40;border:0px;outline:none;height:1.75em;box-shadow:#dee2e6 0px 0px 0px 1px inset;float:left;padding:1px;padding-inline-start:0.5em;appearance:none}:root[dir="ltr"] .color-picker-input{border-radius:0px 4px 4px 0px}:root[dir="rtl"] .color-picker-input{border-radius:4px 0px 0px 4px}.excalidraw .color-picker-label-swatch{height:1.875rem;width:1.875rem;margin-inline-end:0.25rem;border:1px solid #dee2e6;position:relative;overflow:hidden;background-color:transparent !important}.excalidraw .color-picker-label-swatch:after{content:"";position:absolute;top:0;left:0;width:100%;height:100%;background:var(--swatch-color)}.color-picker-keybinding{position:absolute;bottom:2px;font-size:0.7em;color:#ccc}:root[dir="ltr"] .color-picker-keybinding{right:2px}:root[dir="rtl"] .color-picker-keybinding{left:2px} - -.TextInput{display:inline-block;border:1.5px solid #e9ecef;line-height:1;padding:0.75rem;white-space:nowrap;border-radius:var(--space-factor);background-color:#fff}.TextInput:not(:focus):hover{background-color:#f1f3f5}.TextInput:focus{outline:none;box-shadow:0 0 0 2px #339af0} - -.Avatar{width:2.5rem;height:2.5rem;border-radius:1.25rem;display:flex;justify-content:center;align-items:center;color:#fff;cursor:pointer;font-size:0.8rem;font-weight:500} - -.Island{--padding: 0;background-color:var(--bg-color-island);backdrop-filter:saturate(100%) blur(10px);box-shadow:var(--shadow-island);border-radius:var(--border-radius-m);padding:calc(var(--padding) * var(--space-factor));position:relative;transition:box-shadow 0.5s ease-in-out}.Island.zen-mode{box-shadow:none} - -.Stack{--gap: 0;display:grid;gap:calc(var(--space-factor) * var(--gap))}.Stack_vertical{grid-template-columns:auto;grid-auto-flow:row;grid-auto-rows:min-content}.Stack_horizontal{grid-template-rows:auto;grid-auto-flow:column;grid-auto-columns:min-content} - -.FixedSideContainer{--margin: 0.25rem;position:absolute;pointer-events:none}.FixedSideContainer>*{pointer-events:all}.FixedSideContainer_side_top{left:var(--margin);top:var(--margin);right:var(--margin);z-index:2}.FixedSideContainer_side_top.zen-mode{right:42px} - -.UserList{pointer-events:none;padding:var(--space-factor) 40px var(--space-factor) var(--space-factor);display:flex;flex-wrap:wrap;justify-content:flex-end}.UserList>*{pointer-events:all;margin:0 0 var(--space-factor) var(--space-factor)}.UserList_mobile{padding:0;justify-content:normal}.UserList_mobile>*{margin:0 var(--space-factor) var(--space-factor) 0} - -.ExportDialog__preview{--preview-padding: calc(var(--space-factor) * 4);background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMUlEQVQ4T2NkYGAQYcAP3uCTZhw1gGGYhAGBZIA/nYDCgBDAm9BGDWAAJyRCgLaBCAAgXwixzAS0pgAAAABJRU5ErkJggg==") left center;text-align:center;padding:var(--preview-padding);margin-bottom:calc(var(--space-factor) * 3)}.ExportDialog__preview canvas{max-width:calc(100% - var(--preview-padding) * 2);max-height:25rem}.ExportDialog__actions{width:100%;display:flex;grid-gap:calc(var(--space-factor) * 2);align-items:top;justify-content:space-between}.ExportDialog__name{grid-column:project-name;margin:auto}@media (max-width: 550px){.ExportDialog{display:flex;flex-direction:column}.ExportDialog__actions{flex-direction:column;align-items:center}.ExportDialog__actions>*{margin-bottom:calc(var(--space-factor) * 3)}}@media (max-width: 600px), (max-height: 500px) and (max-width: 1000px){.ExportDialog__preview canvas{max-height:30vh}.ExportDialog__dialog,.ExportDialog__dialog .Island{height:100%;box-sizing:border-box}.ExportDialog__dialog .Island{overflow-y:auto}} - -.Modal{position:fixed;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center;overflow:auto;padding:calc(var(--space-factor) * 10)}.Modal__background{position:fixed;top:0;left:0;right:0;bottom:0;z-index:1;background-color:rgba(0,0,0,0.3);backdrop-filter:blur(2px)}.Modal__content{position:relative;z-index:2;width:100%;max-width:var(--max-width);opacity:0;transform:translateY(10px);animation:Modal__content_fade-in 0.1s ease-out 0.05s forwards;position:relative;background:#fff;backdrop-filter:none}@media (max-width: 600px), (max-height: 500px) and (max-width: 1000px){.Modal__content{max-width:100%}}@keyframes Modal__content_fade-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.Modal__close{width:calc(var(--space-factor) * 7);height:calc(var(--space-factor) * 7);display:flex;align-items:center;justify-content:center}.Modal__close svg{height:calc(var(--space-factor) * 5)}.Modal__close--floating{position:absolute;right:calc(var(--space-factor) * 5);top:calc(var(--space-factor) * 5)}@media (max-width: 600px), (max-height: 500px) and (max-width: 1000px){.Modal{padding:0}.Modal__content{position:fixed;top:0;left:0;right:0;bottom:0}} - -.Dialog__title{display:grid;align-items:center;margin-top:0;grid-template-columns:1fr calc(var(--space-factor) * 7);grid-gap:var(--metric)}.Dialog__titleContent{flex:1}.Dialog .Modal__close{margin:0}@media (max-width: 600px), (max-height: 500px) and (max-width: 1000px){.Dialog{--metric: calc(var(--space-factor) * 4);--inset-left: max(var(--metric), var(--sal));--inset-right: max(var(--metric), var(--sar))}.Dialog__title{grid-template-columns:calc(var(--space-factor) * 7) 1fr calc( var(--space-factor) * 7);position:sticky;top:calc(-1 * var(--metric));margin:calc(-1 * var(--inset-right));margin-top:calc(-1 * var(--metric));margin-bottom:var(--metric);padding:calc(var(--space-factor) * 2);padding-left:var(--inset-left);padding-right:var(--inset-right);background:white;font-size:1.25em;box-sizing:border-box;border-bottom:1px solid #ced4da;z-index:1}.Dialog__titleContent{text-align:center}.Dialog .Island{width:100vw;height:100%;box-sizing:border-box;overflow-y:auto;padding-left:max(calc(var(--padding) * var(--space-factor)), var(--sal));padding-right:max(calc(var(--padding) * var(--space-factor)), var(--sar));padding-bottom:max(calc(var(--padding) * var(--space-factor)), var(--sab))}.Dialog .Modal__close{order:-1}} - -.HintViewer{color:#868e96;font-size:0.8rem;left:50%;pointer-events:none;position:absolute;top:54px;transform:translateX(calc(-50% - 16px));white-space:pre;text-align:center}@media (max-width: 600px), (max-height: 500px) and (max-width: 1000px){.HintViewer{position:static;transform:none;margin-top:0.5rem}}.HintViewer>span{background-color:rgba(255,255,255,0.88);padding:0.2rem 0.4rem;border-radius:3px} - -.RoomDialog-modalButton.is-collaborating{background-color:#ebfbee;color:#2b8a3e}.RoomDialog-modalButton-collaborators{min-width:1em;position:absolute;bottom:-5px;right:-5px;padding:3px;border-radius:50%;background-color:#40c057;color:#fff;font-size:0.7em;font-family:var(--ui-font)}.RoomDialog-linkContainer{display:flex;margin:1.5em 0}.RoomDialog-link{min-width:0;flex:1 1 auto;margin-left:1em;display:inline-block;cursor:pointer;border:none;height:2.5rem;line-height:2.5rem;padding:0 0.5rem;white-space:nowrap;border-radius:var(--space-factor);background-color:var(--button-gray-1)}.RoomDialog-usernameContainer{display:flex;margin:1.5em 0;display:flex;align-items:center;justify-content:center}.RoomDialog-username{appearance:none;min-width:0;flex:1 1 auto;margin-left:1em;height:2.5rem;font-size:1em;line-height:1.5;padding:0 0.5rem}.RoomDialog-sessionStartButtonContainer{display:flex;justify-content:center}.Modal .RoomDialog-stopSession{background-color:#ffe3e3;color:#c92a2a} - -.Tooltip{position:relative}.Tooltip__label{--arrow-size: 4px;visibility:hidden;width:10ch;background:#000;color:#fff;text-align:center;border-radius:4px;padding:4px;position:absolute;z-index:10;font-size:0.7rem;line-height:1.5;top:calc(100% + var(--arrow-size) + 3px);left:calc(-50% + var(--arrow-size) / 2 - 1px);word-wrap:break-word}.Tooltip__label::after{content:"";border:var(--arrow-size) solid transparent;border-bottom-color:#000;position:absolute;bottom:100%;left:calc(50% - var(--arrow-size))}body:active .Tooltip:not(:hover){pointer-events:none}body:not(:active) .Tooltip:hover .Tooltip__label{visibility:visible}.Tooltip__label:hover{visibility:visible} - -.layer-ui__wrapper .encrypted-icon{position:relative;margin-inline-start:15px;display:flex;justify-content:center;align-items:center;border-radius:var(--space-factor);color:#2b8a3e}.layer-ui__wrapper .encrypted-icon svg{width:1.2rem;height:1.2rem}.layer-ui__wrapper .encrypted-icon.tooltip .tooltip-text{visibility:hidden;width:20rem;bottom:calc(50% + 0.8rem + 6px);background-color:#000;color:#fff;text-align:center;border-radius:6px;padding:5px;position:absolute;z-index:10;font-size:13px;line-height:1.5;white-space:pre-wrap}:root[dir="ltr"] .layer-ui__wrapper .encrypted-icon.tooltip .tooltip-text{left:-5px}:root[dir="rtl"] .layer-ui__wrapper .encrypted-icon.tooltip .tooltip-text{right:-5px}.layer-ui__wrapper .encrypted-icon.tooltip .tooltip-text::after{--size: 6px;content:"";border:var(--size) solid transparent;border-top-color:#000;position:absolute;bottom:calc(-2 * var(--size))}:root[dir="ltr"] .layer-ui__wrapper .encrypted-icon.tooltip .tooltip-text::after{left:calc(5px + var(--size) / 2)}:root[dir="rtl"] .layer-ui__wrapper .encrypted-icon.tooltip .tooltip-text::after{right:calc(5px + var(--size) / 2)}body:active .layer-ui__wrapper .encrypted-icon.tooltip:not(:hover){pointer-events:none}body:not(:active) .layer-ui__wrapper .encrypted-icon.tooltip:hover .tooltip-text{visibility:visible}.layer-ui__wrapper .encrypted-icon .tooltip-text:hover{visibility:visible}.layer-ui__wrapper__github-corner{top:0;position:absolute;width:40px}:root[dir="ltr"] .layer-ui__wrapper__github-corner{right:0}:root[dir="rtl"] .layer-ui__wrapper__github-corner{left:0}.layer-ui__wrapper__footer{position:absolute;bottom:0px;width:190px}:root[dir="ltr"] .layer-ui__wrapper__footer{right:0}:root[dir="rtl"] .layer-ui__wrapper__footer{left:0}.layer-ui__wrapper .zen-mode-transition{transition:transform 0.5s ease-in-out}:root[dir="ltr"] .layer-ui__wrapper .zen-mode-transition.transition-left{transform:translate(-999px, 0)}:root[dir="ltr"] .layer-ui__wrapper .zen-mode-transition.transition-right{transform:translate(999px, 0px)}:root[dir="rtl"] .layer-ui__wrapper .zen-mode-transition.transition-left{transform:translate(999px, 0)}:root[dir="rtl"] .layer-ui__wrapper .zen-mode-transition.transition-right{transform:translate(-999px, 0)}.layer-ui__wrapper .zen-mode-transition.App-menu_bottom--transition-left{transform:translate(-92px, 0)}.layer-ui__wrapper .disable-zen-mode{height:30px;position:absolute;bottom:10px;right:15px;font-size:10px;padding:10px;font-weight:500;opacity:0;visibility:hidden;transition:visibility 0s linear 0s, opacity 0.5s}.layer-ui__wrapper .disable-zen-mode--visible{opacity:1;visibility:visible;transition:visibility 0s linear 300ms, opacity 0.5s;transition-delay:0.8s} - -@font-face{font-family:"Virgil";src:url(FG_Virgil.woff2);font-display:swap}@font-face{font-family:"Cascadia";src:url(Cascadia.woff2);font-display:swap} - -.visually-hidden{position:absolute !important;height:1px;width:1px;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);white-space:nowrap}.LoadingMessage{position:absolute;top:0;right:0;bottom:0;left:0;z-index:999;display:flex;align-items:center;justify-content:center;pointer-events:none}.LoadingMessage span{background-color:rgba(255,255,255,0.8);border-radius:5px;padding:0.8em 1.2em;font-size:1.3em} - -:root{--text-color-primary: #343a40;--bg-color-main: #fff;--shadow-island: 0 1px 5px rgba(0, 0, 0, 0.15);--bg-color-island: rgba(255, 255, 255, 0.9);--border-radius-m: 4px;--space-factor: 0.25rem}:root{--sat: env(safe-area-inset-top);--sab: env(safe-area-inset-bottom);--sal: env(safe-area-inset-left);--sar: env(safe-area-inset-right);--text-color-primary: #343a40;--bg-color-main: #fff;--shadow-island: 0 1px 5px rgba(0,0,0,0.15);--border-radius-m: 4px;--space-factor: 0.25rem}.excalidraw{display:flex;position:fixed;top:0;bottom:0;left:0;right:0}.excalidraw [contenteditable]{user-select:auto;cursor:text}.excalidraw canvas{touch-action:none;user-select:none;image-rendering:pixelated;image-rendering:-moz-crisp-edges}.excalidraw .FixedSideContainer{padding-top:var(--sat, 0px);padding-right:var(--sar, 0px);padding-bottom:var(--sab, 0px);padding-left:var(--sal, 0px)}.excalidraw .panelRow{display:flex;justify-content:space-between}.excalidraw .panelColumn{display:flex;flex-direction:column}.excalidraw .panelColumn h3,.excalidraw .panelColumn legend,.excalidraw .panelColumn .control-label{margin-top:0.333rem;margin-bottom:0.333rem;font-size:0.75rem;color:var(--text-color-primary);font-weight:bold;display:block}.excalidraw .panelColumn .control-label input{display:block;width:100%}.excalidraw .panelColumn h3:first-child,.excalidraw .panelColumn legend:first-child,.excalidraw .panelColumn .control-label:first-child{margin-top:0}.excalidraw .panelColumn legend{padding:0}.excalidraw .panelColumn .buttonList{flex-wrap:wrap}.excalidraw .panelColumn .buttonList label{margin-right:0.25rem;font-size:0.75rem;display:inline-block}.excalidraw .panelColumn .buttonList input[type="radio"]{opacity:0;position:absolute;pointer-events:none}.excalidraw .panelColumn .buttonList .ToolIcon{margin:0 5px}.excalidraw .panelColumn .buttonList .ToolIcon__icon{width:28px;height:28px}.excalidraw .panelColumn fieldset{margin:0;margin-top:0.333rem;padding:0;border:none}.excalidraw .divider{width:1px;background-color:#e9ecef;margin:1px}.excalidraw .buttonList label:focus-within,.excalidraw input:focus{outline:transparent;box-shadow:0 0 0 2px #a5d8ff}.excalidraw .active,.excalidraw .buttonList label.active{background-color:#ced4da}.excalidraw .active:hover,.excalidraw .buttonList label.active:hover{background-color:#ced4da}.excalidraw .active:active,.excalidraw .buttonList label.active:active{background-color:#adb5bd}.excalidraw .App-bottom-bar{position:absolute;top:0;bottom:0;left:0;right:0;--bar-padding: calc(4 * var(--space-factor));padding-top:max(var(--bar-padding), var(--sat, 0px));padding-right:var(--sar, 0px);padding-bottom:var(--sab, 0px);padding-left:var(--sal, 0px);z-index:4;display:flex;align-items:flex-end;pointer-events:none}.excalidraw .App-bottom-bar>.Island{width:100%;max-width:100%;min-width:100%;box-sizing:border-box;max-height:100%;display:flex;flex-direction:column;pointer-events:initial}.excalidraw .App-toolbar{width:100%;box-sizing:border-box}.excalidraw .App-toolbar-content{display:flex;align-items:center;justify-content:space-between}.excalidraw .App-mobile-menu{width:100%;overflow-x:visible;overflow-y:auto;box-sizing:border-box;margin-bottom:var(--bar-padding)}.excalidraw .App-menu{display:grid}.excalidraw .App-menu_top{grid-template-columns:1fr auto 1fr;grid-gap:4px;align-items:flex-start;cursor:default;pointer-events:none !important}.excalidraw .App-menu_top>*{pointer-events:all}.excalidraw .App-menu_top>*:first-child{justify-self:flex-start}.excalidraw .App-menu_top>*:last-child{justify-self:flex-end}.excalidraw .App-menu_bottom{position:absolute;bottom:0;grid-template-columns:1fr auto 1fr;grid-gap:4px;align-items:flex-start;cursor:default;pointer-events:none !important}.excalidraw .App-menu_bottom--transition-left section{width:185px}.excalidraw .App-menu_bottom section{display:flex}.excalidraw .App-menu_bottom>*{pointer-events:all}.excalidraw .App-menu_bottom>*:first-child{justify-self:flex-start}.excalidraw .App-menu_bottom>*:last-child{justify-self:flex-end}.excalidraw .App-menu_left{grid-template-rows:1fr auto 1fr;height:100%}.excalidraw .App-menu_right{grid-template-rows:1fr;height:100%}.excalidraw .App-menu__left{overflow-y:auto;max-height:calc(100vh - 236px)}.excalidraw .ErrorSplash{min-height:100vh;padding:20px 0;overflow:auto;display:flex;align-items:center;justify-content:center;user-select:text}.excalidraw .ErrorSplash .ErrorSplash-messageContainer{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;background-color:#ffe3e3;border:3px solid #c92a2a}.excalidraw .ErrorSplash .ErrorSplash-paragraph{margin:15px 0;max-width:600px}.excalidraw .ErrorSplash .ErrorSplash-paragraph.align-center{text-align:center}.excalidraw .ErrorSplash .bigger,.excalidraw .ErrorSplash .bigger button{font-size:1.1em}.excalidraw .ErrorSplash .smaller,.excalidraw .ErrorSplash .smaller button{font-size:0.9em}.excalidraw .ErrorSplash .ErrorSplash-details{display:flex;flex-direction:column;align-items:flex-start}.excalidraw .ErrorSplash .ErrorSplash-details textarea{width:100%;margin:10px 0;font-family:"Cascadia";font-size:0.8em}.excalidraw .dropdown-select{height:1.5rem;padding:0;padding-inline-start:0.5rem;padding-inline-end:1.5rem;background-color:#e9ecef;border-radius:var(--space-factor);border:1px solid #ced4da;font-size:0.8rem;outline:none;appearance:none;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-position:right 0.7rem top 50%, 0 0;background-size:0.65em auto, 100%}:root[dir="rtl"] .excalidraw .dropdown-select{background-position:left 0.7rem top 50%, 0 0}.excalidraw .dropdown-select:focus{box-shadow:0 0 0 2px #a5d8ff}.excalidraw .dropdown-select:hover{background-color:#ced4da}.excalidraw .dropdown-select:active{background-color:#ced4da}.excalidraw .dropdown-select.dropdown-select--floating{position:absolute;margin-right:0.5em}.excalidraw .dropdown-select__language.dropdown-select--floating{position:fixed;bottom:10px}:root[dir="ltr"] .excalidraw .dropdown-select__language.dropdown-select--floating{right:44px}:root[dir="rtl"] .excalidraw .dropdown-select__language.dropdown-select--floating{left:44px}.excalidraw .zIndexButton{margin:0 5px;padding:5px;display:inline-flex;align-items:center;justify-content:center}.excalidraw .zIndexButton svg{width:18px;height:18px}.excalidraw .scroll-back-to-content{position:fixed;left:50%;bottom:30px;transform:translateX(-50%);padding:10px 20px}.excalidraw .help-icon{position:absolute;cursor:pointer;fill:#868e96;bottom:14px}:root[dir="ltr"] .excalidraw .help-icon{right:14px}:root[dir="rtl"] .excalidraw .help-icon{left:14px}@media (max-width: 600px), (max-height: 500px) and (max-width: 1000px){.excalidraw aside{display:none}.excalidraw .scroll-back-to-content{bottom:calc(80px + var(--sab, 0px));z-index:-1}}:root[dir="rtl"] .excalidraw .rtl-mirror{transform:scaleX(-1)}.excalidraw .github-corner{position:absolute;top:0;z-index:2}:root[dir="ltr"] .excalidraw .github-corner{right:0}:root[dir="rtl"] .excalidraw .github-corner{left:0}.excalidraw .zen-mode-visibility{visibility:visible;opacity:1;height:auto;width:auto;transition:opacity 0.5s}.excalidraw .zen-mode-visibility.zen-mode-visibility--hidden{visibility:hidden;opacity:0;height:0;width:0;transition:opacity 0.5s}.excalidraw .disable-pointerEvents{pointer-events:none !important}.excalidraw a,.Modal a{font-weight:500;text-decoration:none;color:#1c7ed6}.excalidraw a:hover,.Modal a:hover{text-decoration:underline}.excalidraw button,.excalidraw .buttonList label,.Modal button,.Modal .buttonList label{user-select:none;background-color:#e9ecef;border:0;border-radius:4px;margin:0.125rem 0;padding:0.25rem;white-space:nowrap;cursor:pointer}.excalidraw button:focus,.excalidraw .buttonList label:focus,.Modal button:focus,.Modal .buttonList label:focus{outline:transparent;box-shadow:0 0 0 2px #a5d8ff}.excalidraw button:hover,.excalidraw .buttonList label:hover,.Modal button:hover,.Modal .buttonList label:hover{background-color:#ced4da}.excalidraw button:active,.excalidraw .buttonList label:active,.Modal button:active,.Modal .buttonList label:active{background-color:#adb5bd}.excalidraw button:disabled,.excalidraw .buttonList label:disabled,.Modal button:disabled,.Modal .buttonList label:disabled{cursor:not-allowed}@media print{.App-bottom-bar,.FixedSideContainer,.layer-ui__wrapper{display:none}} - +.excalidraw .Dialog{-webkit-user-select:text;user-select:text;cursor:auto}.excalidraw .Dialog__title{display:grid;align-items:center;margin-top:0;grid-template-columns:1fr calc(var(--space-factor)*7);grid-gap:var(--metric);padding:calc(var(--space-factor)*2);text-align:center;font-feature-settings:"smcp";font-variant:small-caps;font-size:1.2em}.excalidraw .Dialog__titleContent{flex:1 1}.excalidraw .Dialog .Modal__close{color:var(--icon-fill-color);margin:0}.excalidraw .Dialog__content{padding:0 16px 16px}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .Dialog{--metric:calc(var(--space-factor)*4);--inset-left:max(var(--metric),var(--sal));--inset-right:max(var(--metric),var(--sar))}.excalidraw .Dialog__title{grid-template-columns:calc(var(--space-factor)*7) 1fr calc(var(--space-factor)*7);position:-webkit-sticky;position:sticky;top:0;padding:calc(var(--space-factor)*2);background:var(--island-bg-color);font-size:1.25em;box-sizing:border-box;border-bottom:1px solid var(--button-gray-2);z-index:1}.excalidraw .Dialog__titleContent{text-align:center}.excalidraw .Dialog .Island{width:100vw;height:100%;box-sizing:border-box;overflow-y:auto;padding-left:max(calc(var(--padding)*var(--space-factor)),var(--sal));padding-right:max(calc(var(--padding)*var(--space-factor)),var(--sar));padding-bottom:max(calc(var(--padding)*var(--space-factor)),var(--sab))}.excalidraw .Dialog .Modal__close{order:-1}}.excalidraw .Island{--padding:0;background-color:var(--island-bg-color);-webkit-backdrop-filter:saturate(100%) blur(10px);backdrop-filter:saturate(100%) blur(10px);box-shadow:var(--shadow-island);border-radius:4px;padding:calc(var(--padding)*var(--space-factor));position:relative;transition:box-shadow .5s ease-in-out}.excalidraw .Island.zen-mode{box-shadow:none}.excalidraw.excalidraw-modal-container{position:absolute;z-index:10}.excalidraw .Modal{position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center;overflow:auto;padding:calc(var(--space-factor)*10)}.excalidraw .Modal__background{position:absolute;top:0;left:0;right:0;bottom:0;z-index:1;background-color:rgba(0,0,0,.3);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.excalidraw .Modal__content{z-index:2;width:100%;max-width:var(--max-width);max-height:100%;opacity:0;transform:translateY(10px);animation:Modal__content_fade-in .1s ease-out .05s forwards;position:relative;overflow-y:auto;background:var(--island-bg-color);-webkit-backdrop-filter:none;backdrop-filter:none;border:1px solid var(--dialog-border-color);box-shadow:0 2px 10px rgba(0,0,0,.25);border-radius:6px}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .Modal__content{max-width:100%;border:0;border-radius:0}}@keyframes Modal__content_fade-in{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.excalidraw .Modal__close{width:calc(var(--space-factor)*7);height:calc(var(--space-factor)*7);display:flex;align-items:center;justify-content:center}.excalidraw .Modal__close svg{height:calc(var(--space-factor)*5)}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .Modal{padding:0}.excalidraw .Modal__content{position:absolute;top:0;left:0;right:0;bottom:0}}.excalidraw .ToolIcon{display:inline-flex;position:relative;font-family:Cascadia;cursor:pointer;background-color:var(--button-gray-1);-webkit-tap-highlight-color:transparent}.excalidraw .ToolIcon,.excalidraw .ToolIcon__icon{align-items:center;border-radius:var(--space-factor)}.excalidraw .ToolIcon__icon{width:2.5rem;height:2.5rem;color:var(--icon-fill-color);display:flex;justify-content:center}.excalidraw .ToolIcon__icon svg{position:relative;height:1em;fill:var(--icon-fill-color);color:var(--icon-fill-color)}.excalidraw .ToolIcon__icon+.ToolIcon__label{margin-inline-start:0}.excalidraw .ToolIcon__label{color:var(--icon-fill-color);font-family:var(--ui-font);margin:0 .8em;text-overflow:ellipsis}.excalidraw .ToolIcon_size_s .ToolIcon__icon{width:1.4rem;height:1.4rem;font-size:.8em}.excalidraw .excalidraw .ToolIcon_type_button,.excalidraw .Modal .ToolIcon_type_button,.excalidraw .ToolIcon_type_button{padding:0;border:none;margin:0;font-size:inherit}.excalidraw .excalidraw .ToolIcon_type_button:hover,.excalidraw .Modal .ToolIcon_type_button:hover,.excalidraw .ToolIcon_type_button:hover{background-color:var(--button-gray-1)}.excalidraw .excalidraw .ToolIcon_type_button:active,.excalidraw .Modal .ToolIcon_type_button:active,.excalidraw .ToolIcon_type_button:active{background-color:var(--button-gray-2)}.excalidraw .excalidraw .ToolIcon_type_button:focus,.excalidraw .Modal .ToolIcon_type_button:focus,.excalidraw .ToolIcon_type_button:focus{box-shadow:0 0 0 2px var(--focus-highlight-color)}.excalidraw .excalidraw .ToolIcon_type_button.ToolIcon--selected,.excalidraw .Modal .ToolIcon_type_button.ToolIcon--selected,.excalidraw .ToolIcon_type_button.ToolIcon--selected{background-color:var(--button-gray-2)}.excalidraw .excalidraw .ToolIcon_type_button.ToolIcon--selected:active,.excalidraw .Modal .ToolIcon_type_button.ToolIcon--selected:active,.excalidraw .ToolIcon_type_button.ToolIcon--selected:active{background-color:var(--button-gray-3)}.excalidraw .excalidraw .ToolIcon_type_button--show,.excalidraw .Modal .ToolIcon_type_button--show,.excalidraw .ToolIcon_type_button--show{visibility:visible}.excalidraw .excalidraw .ToolIcon_type_button--hide,.excalidraw .Modal .ToolIcon_type_button--hide,.excalidraw .ToolIcon_type_button--hide{visibility:hidden}.excalidraw .ToolIcon_type_checkbox,.excalidraw .ToolIcon_type_radio{position:absolute;opacity:0;pointer-events:none}.excalidraw .ToolIcon_type_checkbox:not(.ToolIcon_toggle_opaque):checked+.ToolIcon__icon,.excalidraw .ToolIcon_type_radio:not(.ToolIcon_toggle_opaque):checked+.ToolIcon__icon{background-color:var(--button-gray-2)}.excalidraw .ToolIcon_type_checkbox:focus+.ToolIcon__icon,.excalidraw .ToolIcon_type_radio:focus+.ToolIcon__icon{box-shadow:0 0 0 2px var(--focus-highlight-color)}.excalidraw .ToolIcon_type_checkbox:active+.ToolIcon__icon,.excalidraw .ToolIcon_type_radio:active+.ToolIcon__icon{background-color:var(--button-gray-3)}.excalidraw .ToolIcon_type_floating,.excalidraw .ToolIcon_type_floating:active,.excalidraw .ToolIcon_type_floating:hover{background-color:initial}.excalidraw .ToolIcon_type_floating:focus{box-shadow:none}.excalidraw .ToolIcon_type_floating .ToolIcon__icon{width:2rem;height:2em}.excalidraw .ToolIcon.ToolIcon__lock.ToolIcon_type_floating{margin-left:.1rem}.excalidraw .ToolIcon__keybinding{position:absolute;bottom:2px;right:3px;font-size:.5em;color:var(--keybinding-color);font-family:var(--ui-font);-webkit-user-select:none;user-select:none}@media(max-width:425px){.excalidraw .Shape .ToolIcon__icon{width:2rem;height:2rem}.excalidraw .Shape .ToolIcon__icon svg{height:.8em}}@media(max-width:760px){.excalidraw .ToolIcon.ToolIcon__lock{display:inline-block;position:absolute;top:60px;right:-8px;margin-left:0;border-radius:20px 0 0 20px;z-index:1}.excalidraw .ToolIcon.ToolIcon__lock,.excalidraw .ToolIcon.ToolIcon__lock:hover{background-color:var(--button-gray-1)}.excalidraw .ToolIcon.ToolIcon__lock:active{background-color:var(--button-gray-2)}.excalidraw .ToolIcon.ToolIcon__lock .ToolIcon__icon{border-radius:inherit}.excalidraw .ToolIcon.ToolIcon__lock svg{position:static}}.excalidraw .TooltipIcon{width:.9em;height:.9em;margin-left:5px;margin-top:1px}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .TooltipIcon{display:none}}:root[dir=ltr] .excalidraw .unlocked-icon{left:2px}:root[dir=rtl] .excalidraw .unlocked-icon{right:2px}.excalidraw .popover{position:fixed;z-index:10}.excalidraw .color-picker{background:var(--popup-bg-color);border:0 solid hsla(0,0%,100%,.25);box-shadow:0 1px 4px rgba(0,0,0,.25);border-radius:4px;position:absolute}:root[dir=ltr] .excalidraw .color-picker{left:-5.5px}:root[dir=rtl] .excalidraw .color-picker{right:-5.5px}.excalidraw .color-picker-control-container{display:grid;grid-template-columns:auto 1fr;align-items:center}.excalidraw .color-picker-triangle{width:0;height:0;border-left:9px solid transparent;border-bottom:10px solid var(--popup-bg-color);border-right:9px solid transparent;border-top:0 solid transparent;position:absolute;top:-10px}:root[dir=ltr] .excalidraw .color-picker-triangle{left:12px}:root[dir=rtl] .excalidraw .color-picker-triangle{right:12px}.excalidraw .color-picker-triangle-shadow{border-color:transparent transparent rgba(0,0,0,.1);top:-11px}.excalidraw .color-picker-content{padding:.5rem;display:grid;grid-template-columns:repeat(5,auto);grid-gap:.5rem;border-radius:4px}.excalidraw .color-picker-content:focus{outline:none;box-shadow:0 0 0 2px var(--focus-highlight-color)}.excalidraw .color-picker-content .color-input-container{grid-column:1/span 5}.excalidraw .color-picker-swatch{position:relative;height:1.875rem;width:1.875rem;cursor:pointer;border-radius:4px;margin:0;box-sizing:border-box;border:1px solid #ddd;background-color:currentColor!important;filter:var(--appearance-filter)}.excalidraw .color-picker-swatch:focus{box-shadow:0 0 4px 1px currentColor;border-color:var(--select-highlight-color)}.excalidraw .color-picker-transparent{border-radius:4px;box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);position:absolute;top:0;right:0;bottom:0;left:0}.excalidraw .color-picker-label-swatch,.excalidraw .color-picker-transparent{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMUlEQVQ4T2NkYGAQYcAP3uCTZhw1gGGYhAGBZIA/nYDCgBDAm9BGDWAAJyRCgLaBCAAgXwixzAS0pgAAAABJRU5ErkJggg==") 0}.excalidraw .color-picker-hash{background:var(--input-border-color);height:1.875rem;width:1.875rem;color:var(--input-label-color);display:flex;align-items:center;justify-content:center;position:relative}:root[dir=ltr] .excalidraw .color-picker-hash{border-radius:4px 0 0 4px}:root[dir=rtl] .excalidraw .color-picker-hash{border-radius:0 4px 4px 0}.excalidraw .color-input-container:focus-within .color-picker-hash{box-shadow:0 0 0 2px var(--focus-highlight-color)}.excalidraw .color-input-container:focus-within .color-picker-hash:after,.excalidraw .color-input-container:focus-within .color-picker-hash:before{content:"";width:1px;height:100%;position:absolute;top:0}.excalidraw .color-input-container:focus-within .color-picker-hash:before{background:var(--input-border-color)}:root[dir=ltr] .excalidraw .color-input-container:focus-within .color-picker-hash:before{right:-1px}:root[dir=rtl] .excalidraw .color-input-container:focus-within .color-picker-hash:before{left:-1px}.excalidraw .color-input-container:focus-within .color-picker-hash:after{background:var(--input-bg-color)}:root[dir=ltr] .excalidraw .color-input-container:focus-within .color-picker-hash:after{right:-2px}:root[dir=rtl] .excalidraw .color-input-container:focus-within .color-picker-hash:after{left:-2px}.excalidraw .color-input-container{display:flex}.excalidraw .color-picker-input{width:12ch;margin:0;font-size:1rem;background-color:var(--input-bg-color);color:var(--text-primary-color);border:0;outline:none;height:1.75em;box-shadow:var(--input-border-color) 0 0 0 1px inset;float:left;padding:1px;padding-inline-start:.5em;-webkit-appearance:none;-moz-appearance:none;appearance:none}:root[dir=ltr] .excalidraw .color-picker-input{border-radius:0 4px 4px 0}:root[dir=rtl] .excalidraw .color-picker-input{border-radius:4px 0 0 4px}.excalidraw .color-picker-label-swatch{height:1.875rem;width:1.875rem;margin-inline-end:.25rem;border:1px solid #dee2e6;position:relative;overflow:hidden;background-color:initial!important;filter:var(--appearance-filter)}.excalidraw .color-picker-label-swatch:after{content:"";position:absolute;top:0;left:0;width:100%;height:100%;background:var(--swatch-color)}.excalidraw .color-picker-keybinding{position:absolute;bottom:2px;font-size:.7em}:root[dir=ltr] .excalidraw .color-picker-keybinding{right:2px}:root[dir=rtl] .excalidraw .color-picker-keybinding{left:2px}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .color-picker-keybinding{display:none}}.excalidraw .color-picker-type-canvasBackground .color-picker-keybinding{color:#aaa}.excalidraw .color-picker-type-elementBackground .color-picker-keybinding{color:#fff}.excalidraw .color-picker-swatch[aria-label=transparent] .color-picker-keybinding{color:#aaa}.excalidraw .color-picker-type-elementStroke .color-picker-keybinding{color:#d4d4d4}.excalidraw.Appearance_dark .color-picker-swatch[aria-label=transparent] .color-picker-keybinding,.excalidraw.Appearance_dark .color-picker-type-elementBackground .color-picker-keybinding{color:#000}.excalidraw .picker-container{display:inline-block;box-sizing:border-box;margin-right:.25rem}.excalidraw .picker{background:var(--popup-bg-color);border:0 solid hsla(0,0%,100%,.25);box-shadow:0 1px 4px rgba(0,0,0,.25);border-radius:4px;position:absolute}.excalidraw .picker-container button,.excalidraw .picker button{position:relative;display:flex;align-items:center;justify-content:center}.excalidraw .picker-container button:focus,.excalidraw .picker button:focus{outline:transparent;background-color:var(--button-gray-2)}.excalidraw .picker-container button:focus svg,.excalidraw .picker button:focus svg{opacity:1}.excalidraw .picker-container button:hover,.excalidraw .picker button:hover{background-color:var(--button-gray-2)}.excalidraw .picker-container button:active,.excalidraw .picker button:active{background-color:var(--button-gray-3)}.excalidraw .picker-container button:disabled,.excalidraw .picker button:disabled{cursor:not-allowed}.excalidraw .picker-container button svg,.excalidraw .picker button svg{margin:0;width:36px;height:18px;opacity:.6;pointer-events:none}.excalidraw .picker button{padding:.25rem .28rem .35rem .25rem}.excalidraw .picker-triangle{width:0;height:0;position:relative;top:-10px;z-index:10}:root[dir=ltr] .excalidraw .picker-triangle{left:12px}:root[dir=rtl] .excalidraw .picker-triangle{right:12px}.excalidraw .picker-triangle:before{content:"";position:absolute;border-color:transparent transparent rgba(0,0,0,.1);border-style:solid;border-width:0 9px 10px;top:-1px}.excalidraw .picker-triangle:after{content:"";position:absolute;border-left:9px solid transparent;border-bottom:10px solid var(--popup-bg-color);border-right:9px solid transparent;border-top:0 solid transparent}.excalidraw .picker-content{padding:.5rem;display:grid;grid-auto-flow:column;grid-gap:.5rem;border-radius:4px}:root[dir=rtl] .excalidraw .picker-content{padding:.4rem}.excalidraw .picker-keybinding{position:absolute;bottom:2px;font-size:.7em;color:var(--keybinding-color)}:root[dir=ltr] .excalidraw .picker-keybinding{right:2px}:root[dir=rtl] .excalidraw .picker-keybinding{left:2px}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .picker-keybinding{display:none}}.excalidraw .picker-type-canvasBackground .picker-keybinding{color:#aaa}.excalidraw .picker-type-elementBackground .picker-keybinding{color:#fff}.excalidraw .picker-swatch[aria-label=transparent] .picker-keybinding{color:#aaa}.excalidraw .picker-type-elementStroke .picker-keybinding{color:#d4d4d4}.excalidraw.Appearance_dark .picker-swatch[aria-label=transparent] .picker-keybinding,.excalidraw.Appearance_dark .picker-type-elementBackground .picker-keybinding{color:#000}.excalidraw .TextInput{color:var(--text-primary-color);display:inline-block;border:1.5px solid var(--button-gray-1);line-height:1;padding:.75rem;white-space:nowrap;border-radius:var(--space-factor);background-color:var(--input-bg-color)}.excalidraw .TextInput:not(:focus):hover{background-color:var(--input-hover-bg-color)}.excalidraw .TextInput:focus{outline:none;box-shadow:0 0 0 2px var(--focus-highlight-color)}.excalidraw .Tooltip{position:relative}.excalidraw .Tooltip__label{--arrow-size:4px;visibility:hidden;background:#000;color:#fff;text-align:center;border-radius:6px;padding:8px;position:absolute;z-index:10;font-size:13px;line-height:1.5;font-weight:500;left:calc(50% + var(--arrow-size)/2 - 1px);transform:translateX(-50%);word-wrap:break-word}.excalidraw .Tooltip__label:after{content:"";border:var(--arrow-size) solid transparent;position:absolute;left:calc(50% - var(--arrow-size))}.excalidraw .Tooltip__label--above{bottom:calc(100% + var(--arrow-size) + 3px)}.excalidraw .Tooltip__label--above:after{border-top-color:#000;top:100%}.excalidraw .Tooltip__label--below{top:calc(100% + var(--arrow-size) + 3px)}.excalidraw .Tooltip__label--below:after{border-bottom-color:#000;bottom:100%}.excalidraw .Tooltip:hover .Tooltip__label,.excalidraw .Tooltip__label:hover{visibility:visible}.excalidraw .Avatar{width:2.5rem;height:2.5rem;border-radius:1.25rem;display:flex;justify-content:center;align-items:center;color:#fff;cursor:pointer;font-size:.8rem;font-weight:500}.excalidraw .context-menu{position:relative;border-radius:4px;box-shadow:0 3px 10px rgba(0,0,0,.2);list-style:none;-webkit-user-select:none;user-select:none;margin:-.25rem 0 0 .125rem;padding:.5rem 0;background-color:var(--popup-secondary-bg-color);border:1px solid var(--button-gray-3);cursor:default}.excalidraw .context-menu button{color:var(--popup-text-color)}.excalidraw .context-menu-option{position:relative;width:100%;min-width:9.5rem;margin:0;padding:.25rem 1rem .25rem 1.25rem;text-align:start;border-radius:0;background-color:initial;border:none;white-space:nowrap;display:grid;grid-template-columns:1fr .2fr;align-items:center}.excalidraw .context-menu-option.checkmark:before{position:absolute;left:6px;margin-bottom:1px;content:"✓"}.excalidraw .context-menu-option.dangerous .context-menu-option__label{color:#f03e3e}.excalidraw .context-menu-option .context-menu-option__label{justify-self:start;margin-inline-end:20px}.excalidraw .context-menu-option .context-menu-option__shortcut{justify-self:end;opacity:.6;font-family:inherit;font-size:.7rem}.excalidraw .context-menu-option:hover{color:var(--popup-bg-color);background-color:var(--select-highlight-color)}.excalidraw .context-menu-option:hover.dangerous{background-color:#fa5252}.excalidraw .context-menu-option:hover.dangerous .context-menu-option__label{color:var(--popup-bg-color)}.excalidraw .context-menu-option:focus{z-index:1}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .context-menu-option{display:block}.excalidraw .context-menu-option .context-menu-option__label{margin-inline-end:0}.excalidraw .context-menu-option .context-menu-option__shortcut{display:none}}.excalidraw .context-menu-option-separator{border:none;border-top:1px solid #adb5bd}.excalidraw .Stack{--gap:0;display:grid;grid-gap:calc(var(--space-factor)*var(--gap));gap:calc(var(--space-factor)*var(--gap))}.excalidraw .Stack_vertical{grid-template-columns:auto;grid-auto-flow:row;grid-auto-rows:-webkit-min-content;grid-auto-rows:min-content}.excalidraw .Stack_horizontal{grid-template-rows:auto;grid-auto-flow:column;grid-auto-columns:-webkit-min-content;grid-auto-columns:min-content}.excalidraw .CollabButton.is-collaborating{background-color:var(--button-special-active-bg-color)}.excalidraw .CollabButton.is-collaborating .ToolIcon__icon svg,.excalidraw .CollabButton.is-collaborating .ToolIcon__label{color:var(--icon-green-fill-color)}.excalidraw .CollabButton-collaborators{min-width:1em;position:absolute;bottom:-5px;padding:3px;border-radius:50%;background-color:#40c057;color:#fff;font-size:.7em;font-family:var(--ui-font)}:root[dir=ltr] .excalidraw .CollabButton-collaborators{right:-5px}:root[dir=rtl] .excalidraw .CollabButton-collaborators{left:-5px}.excalidraw .ExportDialog__preview{--preview-padding:calc(var(--space-factor)*4);background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMUlEQVQ4T2NkYGAQYcAP3uCTZhw1gGGYhAGBZIA/nYDCgBDAm9BGDWAAJyRCgLaBCAAgXwixzAS0pgAAAABJRU5ErkJggg==") 0;text-align:center;padding:var(--preview-padding);margin-bottom:calc(var(--space-factor)*3)}.excalidraw .ExportDialog__preview canvas{max-width:calc(100% - var(--preview-padding)*2);max-height:25rem}.excalidraw.Appearance_dark .ExportDialog__preview canvas{filter:none}.excalidraw .ExportDialog__actions{width:100%;display:flex;grid-gap:calc(var(--space-factor)*2);align-items:top;justify-content:space-between}.excalidraw .ExportDialog__name{grid-column:project-name;margin:auto}.excalidraw .ExportDialog__name .TextInput{height:calc(1rem - 3px)}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .ExportDialog{display:flex;flex-direction:column}.excalidraw .ExportDialog__actions{flex-direction:column;align-items:center}.excalidraw .ExportDialog__actions>*{margin-bottom:calc(var(--space-factor)*3)}.excalidraw .ExportDialog__preview canvas{max-height:30vh}.excalidraw .ExportDialog__dialog,.excalidraw .ExportDialog__dialog .Island{height:100%;box-sizing:border-box}.excalidraw .ExportDialog__dialog .Island{overflow-y:auto}}.excalidraw .FixedSideContainer{--margin:0.25rem;position:absolute;pointer-events:none}.excalidraw .FixedSideContainer>*{pointer-events:all}.excalidraw .FixedSideContainer_side_top{left:var(--margin);top:var(--margin);right:var(--margin);z-index:2}.excalidraw .FixedSideContainer_side_top.zen-mode{right:42px}.excalidraw .HintViewer{pointer-events:none;box-sizing:border-box;position:absolute;display:flex;justify-content:center;left:0;top:100%;max-width:100%;width:100%;margin-top:6px;text-align:center;color:#868e96;font-size:.8rem}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .HintViewer{position:static;padding-right:2em}}.excalidraw .HintViewer>span{padding:.2rem .4rem;background-color:var(--overlay-bg-color);border-radius:4px}.excalidraw .layer-ui__library{margin:auto;display:flex;align-items:center;justify-content:center}.excalidraw .layer-ui__library .layer-ui__library-header{display:flex;align-items:center;width:100%;margin:2px 0}.excalidraw .layer-ui__library .layer-ui__library-header button{margin:0 2px}.excalidraw .layer-ui__library .layer-ui__library-header a{margin-inline-start:auto;padding-inline-end:18px;white-space:nowrap}.excalidraw .layer-ui__library-message{padding:10px 20px;max-width:200px}.excalidraw .layer-ui__library-items{max-height:50vh;overflow:auto}.excalidraw .layer-ui__wrapper{z-index:var(--zIndex-layerUI)}.excalidraw .layer-ui__wrapper .encrypted-icon{position:relative;margin-inline-start:15px;display:flex;justify-content:center;align-items:center;border-radius:var(--space-factor);color:#2b8a3e}.excalidraw .layer-ui__wrapper .encrypted-icon svg{width:1.2rem;height:1.2rem}.excalidraw .layer-ui__wrapper__github-corner{top:0;position:absolute;width:40px}:root[dir=ltr] .excalidraw .layer-ui__wrapper__github-corner{right:0}:root[dir=rtl] .excalidraw .layer-ui__wrapper__github-corner{left:0}.excalidraw .layer-ui__wrapper__footer{position:absolute;z-index:100;bottom:0;width:190px}:root[dir=ltr] .excalidraw .layer-ui__wrapper__footer{right:0}:root[dir=rtl] .excalidraw .layer-ui__wrapper__footer{left:0}.excalidraw .layer-ui__wrapper .zen-mode-transition{transition:transform .5s ease-in-out}:root[dir=ltr] .excalidraw .layer-ui__wrapper .zen-mode-transition.transition-left{transform:translate(-999px)}:root[dir=ltr] .excalidraw .layer-ui__wrapper .zen-mode-transition.transition-right,:root[dir=rtl] .excalidraw .layer-ui__wrapper .zen-mode-transition.transition-left{transform:translate(999px)}:root[dir=rtl] .excalidraw .layer-ui__wrapper .zen-mode-transition.transition-right{transform:translate(-999px)}:root[dir=ltr] .excalidraw .layer-ui__wrapper .zen-mode-transition.App-menu_bottom--transition-left{transform:translate(-92px)}:root[dir=rtl] .excalidraw .layer-ui__wrapper .zen-mode-transition.App-menu_bottom--transition-left{transform:translate(92px)}.excalidraw .layer-ui__wrapper .disable-zen-mode{height:30px;position:absolute;bottom:10px;font-size:10px;padding:10px;font-weight:500;opacity:0;visibility:hidden;transition:visibility 0s linear 0s,opacity .5s}[dir=ltr] .excalidraw .layer-ui__wrapper .disable-zen-mode{right:15px}[dir=rtl] .excalidraw .layer-ui__wrapper .disable-zen-mode{left:15px}.excalidraw .layer-ui__wrapper .disable-zen-mode--visible{opacity:1;visibility:visible;transition:visibility 0s linear .3s,opacity .5s;transition-delay:.8s}.excalidraw .library-unit{align-items:center;border:1px solid var(--button-gray-2);display:flex;justify-content:center;position:relative;width:63px;height:63px}.excalidraw .library-unit__dragger{display:flex;height:100%;width:100%}.excalidraw .library-unit__dragger>svg{filter:var(--appearance-filter);flex-grow:1;max-height:100%;max-width:100%}.excalidraw .library-unit__removeFromLibrary,.excalidraw .library-unit__removeFromLibrary:active,.excalidraw .library-unit__removeFromLibrary:hover{align-items:center;background:none;border:none;color:var(--icon-fill-color);display:flex;justify-content:center;margin:0;padding:0;position:absolute;right:5px;top:5px}.excalidraw .library-unit__removeFromLibrary>svg{height:16px;width:16px}.excalidraw .library-unit__pulse{transform:scale(1);animation:library-unit__pulse-animation 1s ease-in infinite}.excalidraw .library-unit__adder{position:absolute;left:50%;top:50%;width:20px;height:20px;margin-left:-10px;margin-top:-10px;pointer-events:none}.excalidraw .library-unit__active{cursor:pointer}@keyframes library-unit__pulse-animation{0%{transform:scale(.95)}50%{transform:scale(1)}to{transform:scale(.95)}}.excalidraw .UserList{pointer-events:none;padding:var(--space-factor) 40px var(--space-factor) var(--space-factor);display:flex;flex-wrap:wrap;justify-content:flex-end}.excalidraw .UserList>*{pointer-events:all;margin:0 0 var(--space-factor) var(--space-factor)}.excalidraw .UserList_mobile{padding:0;justify-content:normal}.excalidraw .UserList_mobile>*{margin:0 var(--space-factor) var(--space-factor) 0}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .PasteChartDialog .Island{display:flex;flex-direction:column}}.excalidraw .PasteChartDialog .container{display:flex;align-items:center;justify-content:space-around;flex-wrap:wrap}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .PasteChartDialog .container{flex-direction:column;justify-content:center}}.excalidraw .PasteChartDialog .ChartPreview{margin:8px;text-align:center;width:192px;height:128px;border-radius:2px;padding:1px;border:1px solid #ced4da;display:flex;align-items:center;justify-content:center;background:transparent}.excalidraw .PasteChartDialog .ChartPreview div{display:inline-block}.excalidraw .PasteChartDialog .ChartPreview svg{max-height:120px;max-width:186px}.excalidraw .PasteChartDialog .ChartPreview:hover{padding:0;border:2px solid #339af0}.excalidraw .HelpDialog h3{border-bottom:1px solid var(--button-gray-2);padding-bottom:4px}.excalidraw .HelpDialog--island{border:1px solid var(--button-gray-2);margin-bottom:16px}.excalidraw .HelpDialog--island-title{margin:0;padding:4px;background-color:var(--button-gray-1);text-align:center}.excalidraw .HelpDialog--shortcut{border-top:1px solid var(--button-gray-2)}.excalidraw .HelpDialog--key{word-break:keep-all;border:1px solid var(--button-gray-2);padding:2px 8px;margin:auto 4px;background-color:var(--button-gray-1);border-radius:2px;font-size:.8em;min-height:26px;box-sizing:border-box;display:flex;align-items:center;font-family:inherit}.excalidraw .HelpDialog--header{display:flex;flex-direction:row;justify-content:space-evenly;margin-bottom:32px;padding-bottom:16px}.excalidraw .HelpDialog--btn{border:1px solid var(--link-color);padding:8px 32px;border-radius:4px}.excalidraw .HelpDialog--btn:hover{text-decoration:none}.excalidraw .Stats{position:absolute;top:64px;right:12px;font-size:12px;z-index:999}.excalidraw .Stats h3{margin:0 24px 8px 0;white-space:nowrap}.excalidraw .Stats .close{float:right;height:16px;width:16px;cursor:pointer}.excalidraw .Stats .close svg{width:100%;height:100%}.excalidraw .Stats table{width:100%}.excalidraw .Stats table th{border-bottom:1px solid var(--input-border-color);padding:4px}.excalidraw .Stats table tr td:nth-child(2){min-width:24px;text-align:right}:root[dir=rtl] .excalidraw .Stats{left:12px;right:auto}:root[dir=rtl] .excalidraw .Stats h3{margin:0 0 8px 24px}:root[dir=rtl] .excalidraw .Stats .close{float:left}.excalidraw .Toast{animation:fade-in .5s;background-color:var(--button-gray-1);border-radius:4px;bottom:10px;box-sizing:border-box;cursor:default;left:50%;margin-left:-150px;padding:4px 0;position:absolute;text-align:center;width:300px;z-index:999999}.excalidraw .Toast__message{color:var(--popup-text-color);white-space:pre-wrap}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.visually-hidden{position:absolute!important;height:1px;width:1px;overflow:hidden;clip:rect(1px,1px,1px,1px);white-space:nowrap}.LoadingMessage{position:absolute;top:0;right:0;bottom:0;left:0;z-index:999;display:flex;align-items:center;justify-content:center;pointer-events:none}.LoadingMessage span{background-color:var(--button-gray-1);border-radius:5px;padding:.8em 1.2em;color:var(--popup-text-color);font-size:1.3em}.excalidraw{--appearance-filter:none;--button-destructive-bg-color:#ffe3e3;--button-destructive-color:#c92a2a;--button-gray-1:#e9ecef;--button-gray-2:#ced4da;--button-gray-3:#adb5bd;--button-special-active-bg-color:#ebfbee;--dialog-border-color:#868e96;--dropdown-icon:url('data:image/svg+xml;charset=utf-8,');--focus-highlight-color:#a5d8ff;--icon-fill-color:#000;--icon-green-fill-color:#2b8a3e;--input-bg-color:#fff;--input-border-color:#dee2e6;--input-hover-bg-color:#f1f3f5;--input-label-color:#495057;--island-bg-color:hsla(0,0%,100%,0.9);--keybinding-color:#adb5bd;--link-color:#1c7ed6;--overlay-bg-color:hsla(0,0%,100%,0.88);--popup-bg-color:#fff;--popup-secondary-bg-color:#f1f3f5;--popup-text-color:#000;--popup-text-inverted-color:#fff;--sab:env(safe-area-inset-bottom);--sal:env(safe-area-inset-left);--sar:env(safe-area-inset-right);--sat:env(safe-area-inset-top);--select-highlight-color:#339af0;--shadow-island:0 1px 5px rgba(0,0,0,0.15);--space-factor:0.25rem;--text-primary-color:#343a40}.excalidraw.Appearance_dark{background:#000}.excalidraw.Appearance_dark.Appearance_dark-background-none{background:none}.excalidraw.Appearance_dark{--appearance-filter:invert(93%) hue-rotate(180deg);--button-destructive-bg-color:#5a0000;--button-destructive-color:#ffa8a8;--button-gray-1:#363636;--button-gray-2:#272727;--button-gray-3:#222;--button-special-active-bg-color:#204624;--dialog-border-color:#212529;--dropdown-icon:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='292.4' height='292.4' viewBox='0 0 292 292'%3E%3Cpath fill='%23ced4da' d='M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z'/%3E%3C/svg%3E");--focus-highlight-color:#228be6;--icon-fill-color:#ced4da;--icon-green-fill-color:#69db7c;--input-bg-color:#121212;--input-border-color:#2e2e2e;--input-hover-bg-color:#181818;--input-label-color:#e9ecef;--island-bg-color:#1e1e1e;--keybinding-color:#868e96;--overlay-bg-color:rgba(52,58,64,0.12);--popup-bg-color:#2c2c2c;--popup-secondary-bg-color:#222;--popup-text-color:#ced4da;--popup-text-inverted-color:#2c2c2c;--select-highlight-color:#4dabf7;--shadow-island:0 1px 5px rgba(0,0,0,0.3);--text-primary-color:#ced4da}:root{--zIndex-canvas:1;--zIndex-wysiwyg:2;--zIndex-layerUI:3}.excalidraw{position:relative;overflow:hidden;color:var(--text-primary-color);display:flex;top:0;bottom:0;left:0;right:0}.excalidraw a{font-weight:500;text-decoration:none;color:var(--link-color)}.excalidraw a:hover{text-decoration:underline}.excalidraw canvas{touch-action:none;-webkit-user-select:none;user-select:none;image-rendering:pixelated;image-rendering:-moz-crisp-edges;z-index:var(--zIndex-canvas)}.excalidraw.Appearance_dark canvas{filter:var(--appearance-filter)}.excalidraw .FixedSideContainer{padding:var(--sat,0) var(--sar,0) var(--sab,0) var(--sal,0)}.excalidraw .panelRow{display:flex;justify-content:space-between}.excalidraw .panelColumn{display:flex;flex-direction:column}.excalidraw .panelColumn .control-label,.excalidraw .panelColumn h3,.excalidraw .panelColumn legend{margin-top:.333rem;margin-bottom:.333rem;font-size:.75rem;color:var(--text-primary-color);font-weight:700;display:block}.excalidraw .panelColumn .control-label input{display:block;width:100%}.excalidraw .panelColumn .control-label:first-child,.excalidraw .panelColumn h3:first-child,.excalidraw .panelColumn legend:first-child{margin-top:0}.excalidraw .panelColumn legend{padding:0}.excalidraw .panelColumn .iconSelectList{flex-wrap:wrap;position:relative}.excalidraw .panelColumn .buttonList{flex-wrap:wrap}.excalidraw .panelColumn .buttonList label{margin-right:.25rem;font-size:.75rem;display:inline-block}.excalidraw .panelColumn .buttonList input[type=button],.excalidraw .panelColumn .buttonList input[type=radio]{opacity:0;position:absolute;pointer-events:none}.excalidraw .panelColumn .buttonList .iconRow{margin-top:8px}.excalidraw .panelColumn .buttonList .ToolIcon{margin:0;margin-inline-end:8px}.excalidraw .panelColumn .buttonList .ToolIcon:focus{outline:transparent;box-shadow:0 0 0 2px var(--focus-highlight-color)}.excalidraw .panelColumn .buttonList .ToolIcon:hover{background-color:var(--button-gray-2)}.excalidraw .panelColumn .buttonList .ToolIcon:active{background-color:var(--button-gray-3)}.excalidraw .panelColumn .buttonList .ToolIcon:disabled{cursor:not-allowed}.excalidraw .panelColumn .buttonList .ToolIcon__icon{width:28px;height:28px}.excalidraw .panelColumn fieldset{margin:.333rem 0 0;padding:0;border:none}.excalidraw .divider{width:1px;background-color:#e9ecef;margin:1px}.excalidraw .buttonList label:focus-within,.excalidraw input:focus{outline:transparent;box-shadow:0 0 0 2px var(--focus-highlight-color)}.excalidraw .buttonList label,.excalidraw button{-webkit-user-select:none;user-select:none;background-color:var(--button-gray-1);border:0;border-radius:4px;margin:.125rem 0;padding:.25rem;white-space:nowrap;cursor:pointer}.excalidraw .buttonList label:focus,.excalidraw button:focus{outline:transparent;box-shadow:0 0 0 2px var(--focus-highlight-color)}.excalidraw .buttonList label:hover,.excalidraw button:hover{background-color:var(--button-gray-2)}.excalidraw .buttonList label:active,.excalidraw button:active{background-color:var(--button-gray-3)}.excalidraw .buttonList label:disabled,.excalidraw button:disabled{cursor:not-allowed}.excalidraw .active,.excalidraw .active:hover,.excalidraw .buttonList label.active,.excalidraw .buttonList label.active:hover{background-color:var(--button-gray-2)}.excalidraw .active:active,.excalidraw .buttonList label.active:active{background-color:var(--button-gray-3)}.excalidraw .buttonList.buttonListIcon label{display:inline-flex;justify-content:center;align-items:center}.excalidraw .buttonList.buttonListIcon label svg{width:36px;height:18px;opacity:.6}.excalidraw .buttonList.buttonListIcon label.active svg{opacity:1}.excalidraw .App-top-bar{z-index:var(--zIndex-layerUI);display:flex;flex-direction:column;align-items:center}.excalidraw .App-bottom-bar{position:absolute;top:0;bottom:0;left:0;right:0;--bar-padding:calc(var(--space-factor)*4);padding:max(var(--bar-padding),var(--sat,0)) var(--sar,0) var(--sab,0) var(--sal,0);z-index:4;display:flex;align-items:flex-end;pointer-events:none}.excalidraw .App-bottom-bar>.Island{width:100%;max-width:100%;min-width:100%;box-sizing:border-box;max-height:100%;display:flex;flex-direction:column;pointer-events:auto}.excalidraw .App-bottom-bar>.Island .panelColumn{padding:8px 8px 0}.excalidraw .App-toolbar{width:100%;box-sizing:border-box}.excalidraw .App-toolbar-content{display:flex;align-items:center;justify-content:space-between;padding:8px}.excalidraw .App-mobile-menu{width:100%;overflow-x:visible;overflow-y:auto;box-sizing:border-box;margin-bottom:var(--bar-padding)}.excalidraw .App-menu{display:grid;color:var(--icon-fill-color)}.excalidraw .App-menu_top{grid-template-columns:1fr auto 1fr;grid-gap:4px;align-items:flex-start;cursor:default;pointer-events:none!important}.excalidraw .layer-ui__wrapper:not(.disable-pointerEvents) .App-menu_top>*{pointer-events:all}.excalidraw .App-menu_top>:first-child{justify-self:flex-start}.excalidraw .App-menu_top>:last-child{justify-self:flex-end}.excalidraw .App-menu_bottom{position:absolute;bottom:0;grid-template-columns:1fr auto 1fr;grid-gap:4px;align-items:flex-start;cursor:default;pointer-events:none!important;z-index:100}:root[dir=ltr] .excalidraw .App-menu_bottom{left:.25rem}:root[dir=rtl] .excalidraw .App-menu_bottom{right:.25rem}.excalidraw .App-menu_bottom--transition-left section{width:185px}.excalidraw .App-menu_bottom section{display:flex}.excalidraw .layer-ui__wrapper:not(.disable-pointerEvents) .App-menu_bottom>*{pointer-events:all}.excalidraw .App-menu_bottom>:first-child{justify-self:flex-start}.excalidraw .App-menu_bottom>:last-child{justify-self:flex-end}.excalidraw .App-menu_left{grid-template-rows:1fr auto 1fr;height:100%}.excalidraw .App-menu_right{grid-template-rows:1fr;height:100%}.excalidraw .App-menu__left{overflow-y:auto}.excalidraw .dropdown-select{height:1.5rem;padding:0;padding-inline-start:.5rem;padding-inline-end:1.5rem;color:var(--icon-fill-color);background-color:var(--button-gray-1);border-radius:var(--space-factor);border:1px solid var(--button-gray-2);font-size:.8rem;outline:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-image:var(--dropdown-icon);background-repeat:no-repeat;background-position:right .7rem top 50%,0 0;background-size:.65em auto,100%}:root[dir=rtl] .excalidraw .dropdown-select{background-position:left .7rem top 50%,0 0}.excalidraw .dropdown-select:focus{box-shadow:0 0 0 2px var(--focus-highlight-color)}.excalidraw .dropdown-select:active,.excalidraw .dropdown-select:hover{background-color:var(--button-gray-2)}.excalidraw .dropdown-select.dropdown-select--floating{position:absolute;margin:.5em}.excalidraw .dropdown-select__language.dropdown-select--floating{position:absolute;bottom:10px}:root[dir=ltr] .excalidraw .dropdown-select__language.dropdown-select--floating{right:44px}:root[dir=rtl] .excalidraw .dropdown-select__language.dropdown-select--floating{left:44px}.excalidraw .zIndexButton{margin:0;margin-inline-end:8px;padding:5px;display:inline-flex;align-items:center;justify-content:center}.excalidraw .zIndexButton svg{width:18px;height:18px}.excalidraw .scroll-back-to-content{color:var(--popup-text-color);position:absolute;left:50%;bottom:30px;transform:translateX(-50%);padding:10px 20px}.excalidraw .help-icon{position:absolute;cursor:pointer;fill:#868e96;bottom:14px;width:1.5rem}:root[dir=ltr] .excalidraw .help-icon{right:14px}:root[dir=rtl] .excalidraw .help-icon{left:14px}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw aside{display:none}.excalidraw .scroll-back-to-content{bottom:calc(80px + var(--sab, 0));z-index:-1}}:root[dir=rtl] .excalidraw .rtl-mirror{transform:scaleX(-1)}.excalidraw .github-corner{position:absolute;top:0;z-index:2}:root[dir=ltr] .excalidraw .github-corner{right:0}:root[dir=rtl] .excalidraw .github-corner{left:0}.excalidraw .zen-mode-visibility{visibility:visible;opacity:1;height:auto;width:auto;transition:opacity .5s}.excalidraw .zen-mode-visibility.zen-mode-visibility--hidden{visibility:hidden;opacity:0;height:0;width:0;transition:opacity .5s}.excalidraw .disable-pointerEvents{pointer-events:none!important}.excalidraw.excalidraw--view-mode .App-menu{display:flex;justify-content:space-between}@media print{.excalidraw .App-bottom-bar,.excalidraw .FixedSideContainer,.excalidraw .layer-ui__wrapper{display:none}}.ErrorSplash.excalidraw{min-height:100vh;padding:20px 0;overflow:auto;display:flex;align-items:center;justify-content:center;-webkit-user-select:text;user-select:text}.ErrorSplash.excalidraw .ErrorSplash-messageContainer{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px;background-color:#ffe3e3;border:3px solid #c92a2a}.ErrorSplash.excalidraw .ErrorSplash-paragraph{margin:15px 0;max-width:600px}.ErrorSplash.excalidraw .ErrorSplash-paragraph.align-center{text-align:center}.ErrorSplash.excalidraw .bigger,.ErrorSplash.excalidraw .bigger button{font-size:1.1em}.ErrorSplash.excalidraw .smaller,.ErrorSplash.excalidraw .smaller button{font-size:.9em}.ErrorSplash.excalidraw .ErrorSplash-details{display:flex;flex-direction:column;align-items:flex-start}.ErrorSplash.excalidraw .ErrorSplash-details textarea{width:100%;margin:10px 0;font-family:"Cascadia";font-size:.8em}:export{isMobileQuery:(max-width:600px),(max-height:500px) and (max-width:1000px);appearanceFilter:invert(93%) hue-rotate(180deg)}.excalidraw .RoomDialog-linkContainer{display:flex;margin:1.5em 0}.excalidraw .RoomDialog-link{color:var(--text-primary-color);min-width:0;flex:1 1 auto;margin-inline-start:1em;display:inline-block;cursor:pointer;border:none;height:2.5rem;line-height:2.5rem;padding:0 .5rem;white-space:nowrap;border-radius:var(--space-factor);background-color:var(--button-gray-1)}.excalidraw .RoomDialog-emoji{font-family:sans-serif}.excalidraw .RoomDialog-usernameContainer{margin:1.5em 0;display:flex;align-items:center;justify-content:center}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .RoomDialog-usernameContainer{flex-direction:column;align-items:stretch}}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .RoomDialog-usernameLabel{font-weight:700}}.excalidraw .RoomDialog-username{background-color:var(--input-bg-color);border-color:var(--input-border-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;min-width:0;flex:1 1 auto;margin-inline-start:1em;height:2.5rem;font-size:1em;line-height:1.5;padding:0 .5rem}@media(max-height:500px)and (max-width:1000px),(max-width:600px){.excalidraw .RoomDialog-username{margin-top:.5em;margin-inline-start:0}}.excalidraw .RoomDialog-sessionStartButtonContainer{display:flex;justify-content:center}.excalidraw .Modal .RoomDialog-stopSession{background-color:var(--button-destructive-bg-color)}.excalidraw .Modal .RoomDialog-stopSession .ToolIcon__icon svg,.excalidraw .Modal .RoomDialog-stopSession .ToolIcon__label{color:var(--button-destructive-color)} +/*# sourceMappingURL=main.b0bde4ac.chunk.css.map */ \ No newline at end of file diff --git a/resources/css/fonts.css b/resources/css/fonts.css new file mode 100644 index 000000000..d78847d10 --- /dev/null +++ b/resources/css/fonts.css @@ -0,0 +1,13 @@ +/* http://www.eaglefonts.com/fg-virgil-ttf-131249.htm */ +@font-face { + font-family: "Virgil"; + src: url("../fonts/Virgil.woff2"); + font-display: swap; +} + +/* https://github.com/microsoft/cascadia-code */ +@font-face { + font-family: "Cascadia"; + src: url("../fonts/Cascadia.woff2"); + font-display: swap; +} diff --git a/resources/css/style.css b/resources/css/style.css index da0a45cfa..3a6e60b31 100644 --- a/resources/css/style.css +++ b/resources/css/style.css @@ -2,6 +2,7 @@ @import "./inter.css"; @import "./reveal.min.css"; @import "./reveal_black.min.css"; +@import "./fonts.css"; @import "./excalidraw.min.css"; @import "./katex.min.css"; @import "./codemirror.min.css"; diff --git a/resources/css/style.dev.css b/resources/css/style.dev.css index 7d9a95075..39523d6d0 100644 --- a/resources/css/style.dev.css +++ b/resources/css/style.dev.css @@ -2,6 +2,7 @@ @import "./inter.css"; @import "./reveal.min.css"; @import "./reveal_black.min.css"; +@import "./fonts.css"; @import "./excalidraw.min.css"; @import "./katex.min.css"; @import "./codemirror.min.css"; @@ -10,4 +11,4 @@ @import "./datepicker.css"; @import "./highlight.css"; @import "./tailwind.core.css"; /* Build by gulp. Check `_buildTailwind` for more detail */ -@import "./common.css"; \ No newline at end of file +@import "./common.css"; diff --git a/resources/css/Cascadia.woff2 b/resources/fonts/Cascadia.woff2 similarity index 100% rename from resources/css/Cascadia.woff2 rename to resources/fonts/Cascadia.woff2 diff --git a/resources/css/FG_Virgil.woff2 b/resources/fonts/FG_Virgil.woff2 similarity index 100% rename from resources/css/FG_Virgil.woff2 rename to resources/fonts/FG_Virgil.woff2 diff --git a/resources/fonts/Virgil.woff2 b/resources/fonts/Virgil.woff2 new file mode 100644 index 000000000..1d61d3fa2 Binary files /dev/null and b/resources/fonts/Virgil.woff2 differ diff --git a/shadow-cljs.edn b/shadow-cljs.edn index 32fea3cc0..006f6078a 100644 --- a/shadow-cljs.edn +++ b/shadow-cljs.edn @@ -12,6 +12,9 @@ :depends-on #{:main}} :age-encryption {:entries [frontend.extensions.age-encryption] + :depends-on #{:main}} + :excalidraw + {:entries [frontend.extensions.excalidraw] :depends-on #{:main}}} :output-dir "./static/js" diff --git a/src/main/frontend/commands.cljs b/src/main/frontend/commands.cljs index f4a659a77..5ab2989f6 100644 --- a/src/main/frontend/commands.cljs +++ b/src/main/frontend/commands.cljs @@ -8,7 +8,9 @@ [goog.dom :as gdom] [goog.object :as gobj] [frontend.format :as format] - [frontend.handler.common :as common-handler])) + [frontend.handler.common :as common-handler] + [frontend.handler.draw :as draw] + [promesa.core :as p])) ;; TODO: move to frontend.handler.editor.commands @@ -102,10 +104,13 @@ ["Scheduled" [[:editor/clear-current-slash] [:editor/show-date-picker]]] ["Query" [[:editor/input "{{query }}" {:backward-pos 2}]]] - ["Draw" [[:editor/input "/draw "] - [:editor/show-input [{:command :draw - :id :title - :placeholder "Draw title"}]]]] + ["Draw" (fn [] + (let [file (draw/file-name) + path (str config/default-draw-directory "/" file) + text (util/format "[[%s]]" path)] + (p/let [_ (draw/create-draw-with-default-content path)] + (println "draw file created, " path)) + text))] ["WAITING" (->marker "WAITING")] ["CANCELED" (->marker "CANCELED")] ["Tomorrow" #(get-page-ref-text (date/tomorrow))] diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index 30043e761..8eaeeb0e8 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -16,7 +16,6 @@ [goog.dom :as gdom] [frontend.handler.expand :as expand] [frontend.components.svg :as svg] - [frontend.components.draw :as draw] [frontend.components.datetime :as datetime-comp] [frontend.ui :as ui] [frontend.handler.editor :as editor-handler] @@ -48,7 +47,8 @@ [frontend.commands :as commands] [lambdaisland.glogi :as log] [frontend.context.i18n :as i18n] - [frontend.template :as template])) + [frontend.template :as template] + [shadow.loader :as loader])) ;; TODO: remove rum/with-context because it'll make reactive queries not working @@ -385,32 +385,41 @@ full-path (.. util/node-path (join repo-path (config/get-local-asset-absolute-path path)))] [:a.asset-ref {:target "_blank" :href full-path} (or title path)])) +(defonce excalidraw-loaded? (atom false)) +(rum/defc excalidraw < rum/reactive + {:init (fn [state] + (p/let [_ (loader/load :excalidraw)] + (reset! excalidraw-loaded? true)) + state)} + [file] + (let [loaded? (rum/react excalidraw-loaded?) + draw-component (if loaded? + (resolve 'frontend.extensions.excalidraw/draw))] + (when draw-component + (draw-component {:file file})))) + (rum/defc page-reference < rum/reactive [html-export? s config label] (let [show-brackets? (state/show-brackets?) nested-link? (:nested-link? config) - contents-page? (= "contents" (string/lower-case (str (:id config))))] - [:span.page-reference - (when (and (or show-brackets? nested-link?) - (not html-export?) - (not contents-page?)) - [:span.text-gray-500.bracket "[["]) - (if (string/ends-with? s ".excalidraw") - [:a.page-ref - {:on-click (fn [e] - (util/stop e) - (set! (.-href js/window.location) - (rfe/href :draw nil {:file (string/replace s (str config/default-draw-directory "/") "")})))} - [:span - (svg/excalidraw-logo) - (string/capitalize (draw/get-file-title s))]] + contents-page? (= "contents" (string/lower-case (str (:id config)))) + draw? (string/ends-with? s ".excalidraw")] + (if (string/ends-with? s ".excalidraw") + [:div.draw {:on-click (fn [e] + (.stopPropagation e))} + (excalidraw s)] + [:span.page-reference + (when (and (or show-brackets? nested-link?) + (not html-export?) + (not contents-page?)) + [:span.text-gray-500.bracket "[["]) (page-cp (assoc config :label (mldoc/plain->text label) - :contents-page? contents-page?) {:page/name s})) - (when (and (or show-brackets? nested-link?) - (not html-export?) - (not contents-page?)) - [:span.text-gray-500.bracket "]]"])])) + :contents-page? contents-page?) {:page/name s}) + (when (and (or show-brackets? nested-link?) + (not html-export?) + (not contents-page?)) + [:span.text-gray-500.bracket "]]"])]))) (defn- latex-environment-content [name option content] diff --git a/src/main/frontend/components/draw.cljs b/src/main/frontend/components/draw.cljs deleted file mode 100644 index 1ff7e2d4c..000000000 --- a/src/main/frontend/components/draw.cljs +++ /dev/null @@ -1,497 +0,0 @@ -(ns frontend.components.draw - (:require [rum.core :as rum] - [goog.object :as gobj] - [frontend.rum :as r] - [frontend.util :as util :refer-macros [profile]] - [frontend.mixins :as mixins] - [frontend.storage :as storage] - [frontend.components.svg :as svg] - [cljs-bean.core :as bean] - [dommy.core :as d] - [clojure.string :as string] - [frontend.handler.notification :as notification] - [frontend.handler.draw :as draw :refer - [*files - *current-file - *current-title - *file-loading? - *elements - *unsaved? - *search-files - *saving-title - *excalidraw]] - [frontend.handler.file :as file] - [frontend.ui :as ui] - [frontend.loader :as loader] - [frontend.config :as config] - [frontend.state :as state] - [frontend.search :as search] - [frontend.components.repo :as repo] - [promesa.core :as p] - [reitit.frontend.easy :as rfe])) - -(defn loaded? [] - js/window.Excalidraw) - -(defonce *loaded? (atom false)) - -(defonce draw-state :draw-state) - -(defn get-draw-state [] - (storage/get draw-state)) -(defn set-draw-state! [value] - (storage/set draw-state value)) -(defn get-k - ([k] - (get-k k (state/get-current-repo))) - ([repo k] - (when repo - (get-in (get-draw-state) [repo k])))) - -(defn set-k - [k v] - (when-let [repo (state/get-current-repo)] - (let [state (get-draw-state)] - (let [new-state (assoc-in state [repo k] v)] - (set-draw-state! new-state))))) - -(defn get-last-file - ([] - (get-k :last-file)) - ([repo] - (get-k repo :last-file))) - -(defn get-last-title - ([] - (get-k :last-title)) - ([repo] - (get-k repo :last-title))) - -(defn set-last-file! - [value] - (set-k :last-file value)) -(defn set-last-title! - [value] - (set-k :last-title value)) - -(defn get-last-elements - [] - (storage/get-json (str (state/get-current-repo) "-" "last-elements"))) -(defn get-last-app-state - [] - (storage/get-json (str (state/get-current-repo) "-" "last-app-state"))) - -(defn set-last-elements! - [value] - (storage/set-json (str (state/get-current-repo) "-" "last-elements") value)) -(defn set-last-app-state! - [value] - (storage/set-json (str (state/get-current-repo) "-" "last-app-state") value)) - -(defn set-excalidraw-component! - [] - (reset! *excalidraw (r/adapt-class - (gobj/get js/window.Excalidraw "default")))) - -(defn serialize-as-json - [elements app-state] - (when (loaded?) - (when-let [f (gobj/get js/window.Excalidraw "serializeAsJSON")] - (f elements app-state)))) - -;; api restore - -(defn from-json - [text] - (when-not (string/blank? text) - (try - (when-let [data (js/JSON.parse text)] - (if (not= "excalidraw" (gobj/get data "type")) - (notification/show! - (util/format "Could not load this invalid excalidraw file") - :error) - {:elements (gobj/get data "elements") - :app-state (gobj/get data "appState")})) - (catch js/Error e - (prn "from json error:") - (js/console.dir e) - (notification/show! - (util/format "Could not load this invalid excalidraw file") - :error))))) - -(defn get-file-title - [file] - (when file - (let [s (subs file 20) - title (string/replace s ".excalidraw" "")] - (string/replace title "-" " ")))) - -(defn save-excalidraw! - [state _event file ok-handler] - (let [title @*current-title] - (cond - (string/blank? title) - (do - (reset! *saving-title nil) - (notification/show! - "Please specify a title first!" - :error) - ;; TODO: focus the title input -) - - (= title @*saving-title) - nil - - :else - (when-let [elements (get-last-elements)] - (reset! *saving-title title) - (let [app-state (get-last-app-state) - [option] (:rum/args state) - file (util/trim-safe - (or - file - @*current-file - (:file option) - (draw/title->file-name title))) - data (serialize-as-json elements app-state)] - (when file - (draw/save-excalidraw! file data - (fn [file] - (reset! *files - (distinct (conj @*files file))) - (reset! *current-file file) - (reset! *unsaved? false) - (set-last-file! file) - (when ok-handler (ok-handler file)) - (reset! *saving-title nil))))))))) - -(defn- clear-canvas! - [] - (when-let [canvas (d/by-id "canvas")] - (let [context (.getContext canvas "2d")] - (.clearRect context 0 0 (gobj/get canvas "width") (gobj/get canvas "height")) - (set! (.-fillStyle context) "#FFF") - (.fillRect context 0 0 (gobj/get canvas "width") (gobj/get canvas "height"))))) - -(defn- new-file! - [] - ;; TODO: save current firstly - (clear-canvas!) - (reset! *current-title "") - (reset! *current-file nil) - (reset! *elements nil) - (set-last-elements! nil) - (set-last-title! nil) - (set-last-file! nil) - (set-last-app-state! nil)) - -(defn- rename-file! - [file new-title] - (when-not (string/blank? new-title) - (let [new-file (draw/title->file-name new-title)] - (when-not (= (string/trim file) (string/trim new-file)) - (save-excalidraw! - {} {} new-file - (fn [] - (set-last-file! new-file) - (util/p-handle - (file/remove-file! - (state/get-current-repo) - (str config/default-draw-directory "/" file)) - (fn [_] - (reset! *files (->> (conj @*files new-file) - (remove #(= file %)) - distinct - (vec))) - (reset! *current-file new-file) - (notification/show! - "File was renamed successfully!" - :success)) - (fn [error] - (println "Rename file failed, reason: ") - (js/console.dir error))))))))) - -(rum/defc draw-title < rum/reactive - (mixins/event-mixin - (fn [state] - (let [old-title @*current-title] - (mixins/hide-when-esc-or-outside - state - :on-hide (fn [state e event] - (let [title (and @*current-title (string/trim @*current-title)) - file @*current-file] - (when (or - (string/blank? old-title) - (not= (string/trim old-title) title)) - (cond - (and file (not (string/blank? title))) - (rename-file! file title) - - (and (not file) - (not (string/blank? title)) - (seq @*elements)) ; new file - (save-excalidraw! {} {} nil nil) - - :else - nil)))))) - state)) - [] - (let [current-title (rum/react *current-title)] - [:input#draw-title.font-medium.w-48.px-2.py-1.ml-2 - {:on-click (fn [e] - (util/stop e)) - :placeholder "Untitled" - :auto-complete "off" - :default-value (or (and current-title (string/capitalize current-title)) "") - :on-change (fn [e] - (when-let [value (util/evalue e)] - (set-last-title! value) - (reset! *current-title value)))}])) - -(rum/defc files-search < rum/reactive - [state] - [:div#search-wrapper.relative.w-full.text-gray-400.focus-within:text-gray-600 - [:div.absolute.inset-y-0.flex.items-center.pointer-events-none.left-3 - [:svg.h-4.w-4 - {:view-box "0 0 20 20", :fill "currentColor"} - [:path - {:d - "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z", - :clip-rule "evenodd", - :fill-rule "evenodd"}]]] - [:input.block.w-full.pl-2.sm:text-sm.sm:leading-3.mb-2.mt-2.border-none.outline-none.focus:outline-none - {:style {:padding-left "2rem" - :border-radius 0} - :placeholder "Search" - :auto-complete "off" - :on-change (fn [e] - (let [value (util/evalue e) - files @*files] - (reset! *search-files - (if (string/blank? value) - files - (search/fuzzy-search files value :limit 10)))))}]]) - -(rum/defcs save-button < rum/reactive - [state] - (let [unsaved? (rum/react *unsaved?)] - [:a.ml-2 {:title (if unsaved? "Save changes" "Save") - :on-click (fn [e] - (save-excalidraw! state e nil nil))} - [:div.ToolIcon__icon {:class (if unsaved? "bg-orange-400" "bg-gray-200") - :style {:width "2rem" - :height "2rem"}} - svg/save]])) - -(rum/defcs files < rum/reactive - [state] - (let [all-files (rum/react *files) - search-files (rum/react *search-files) - files (if (seq search-files) search-files all-files) - current-file (rum/react *current-file) - unsaved? (rum/react *unsaved?)] - [:div.flex-row.flex.items-center - [:a.ml-2 {:title "New file" - :on-click new-file!} - [:div.ToolIcon__icon.bg-gray-200 {:style {:width "2rem" - :height "2rem"}} - svg/plus]] - - (ui/dropdown-with-links - (fn [{:keys [toggle-fn]}] - [:div.ToolIcon__icon.ml-2.cursor.bg-gray-200 {:title "List files" - :on-click toggle-fn - :style {:width "2rem" - :height "2rem"}} - svg/folder]) - (mapv - (fn [file] - {:title (get-file-title file) - :options {:title file - :on-click - (fn [e] - (util/stop e) - (set-last-file! file) - (reset! *current-file file) - (reset! *current-title (get-file-title file)) - (reset! *search-files []))}}) - files) - {:modal-class (util/hiccup->class - "origin-top-right.absolute.left-0.mt-2.rounded-md.shadow-lg.whitespace-no-wrap.bg-white.w-48.dropdown-overflow-auto") - :links-header (when (>= (count all-files) 5) - (files-search))}) - - (save-button) - - (let [links (->> [(when @*current-file - {:title "Delete" - :options {:style {:color "#db1111"} - :on-click (fn [e] - (util/stop e) - (when-let [current-file @*current-file] - (p/let [_ (file/remove-file! (state/get-current-repo) - (str config/default-draw-directory "/" current-file))] - (reset! *files (remove #(= current-file %) @*files)) - (new-file!))))}})] - (remove nil?))] - (when (seq links) - (ui/dropdown-with-links - (fn [{:keys [toggle-fn]}] - [:div.ToolIcon__icon.ml-2.cursor.bg-gray-200 - {:title "More options" - :on-click toggle-fn - :style {:width "2rem" - :height "2rem"}} - (svg/vertical-dots nil)]) - links - {:modal-class (util/hiccup->class - "origin-top-right.absolute.left-0.mt-2.rounded-md.shadow-lg.whitespace-no-wrap.bg-white.w-48.dropdown-overflow-auto")}))) - - (draw-title)])) - -(defn- set-canvas-actions-style! - [state] - (when-let [section (first (d/by-tag "section"))] - (when (= "canvasActions-title" (d/attr section "aria-labelledby")) - (d/set-style! section "margin-top" "48px"))) - state) - -(rum/defcs draw-inner < rum/reactive - (mixins/keyboard-mixin (util/->system-modifier "ctrl+s") - (fn [state e] - (save-excalidraw! state e nil nil))) - (mixins/keyboard-mixin "alt+z" set-canvas-actions-style!) - {:init (fn [state] - (reset! *elements nil) - (let [[option] (:rum/args state) - file (or @*current-file - (:file option))] - (do - (reset! *current-title (get-file-title file)) - (set-last-file! file)) - (cond - file - (do - (reset! *file-loading? true) - (draw/load-excalidraw-file - file - (fn [data] - (let [{:keys [elements app-state]} (from-json data)] - (reset! *elements elements) - (reset! *file-loading? false))))) - - :else - (when-let [elements (get-last-elements)] - ;; TODO: keep this for history undo - (reset! *elements (remove #(gobj/get % "isDeleted") elements)))) - (assoc state - ::layout (atom [js/window.innerWidth js/window.innerHeight])))) - :did-mount set-canvas-actions-style! - :did-update set-canvas-actions-style!} - [state option] - (let [current-repo (state/sub :git/current-repo) - elements (rum/react *elements) - loading? (rum/react *file-loading?) - file (rum/react *current-file) - layout (get state ::layout) - [width height] (rum/react layout) - options (bean/->js {:zenModeEnabled true - :viewBackgroundColor "#FFF"}) - excalidraw-component @*excalidraw] - [:div.draw.white-theme {:style {:background "#FFF"}} - (when (and (or (and file elements) - (nil? file)) - excalidraw-component) - (excalidraw-component - {:width (get option :width width) - :height (get option :height height) - :on-resize (fn [] - (reset! layout [js/window.innerWidth js/window.innerHeight])) - - :on-change (or (:on-change option) - (fn [elements state] - (when (not= (bean/->clj elements) - (bean/->clj @*elements)) - (reset! *unsaved? true)) - (set-last-elements! elements) - (set-last-app-state! state) - (reset! *elements elements))) - :options options - :user (bean/->js {:name (or (:user-name option) - (:name (state/get-me)) - (util/unique-id))}) - :on-username-change (fn []) - :initial-data (or elements #js [])})) - [:div.absolute.top-4.left-4.hidden.md:block - [:div.flex.flex-row.items-center - [:a.mr-3.opacity-70.hover:opacity-100 {:href (rfe/href :home) - :title "Back to logseq"} - (svg/logo false)] - (files) - (when loading? - svg/loading)]] - (ui/notification) - - (when current-repo - [:div.absolute.top-4.right-4.hidden.md:block - [:div.flex.flex-row.items-center - (repo/sync-status current-repo) - (repo/repos-dropdown true - (fn [repo] - (reset! *current-file (get-last-file repo))))]])])) - -(rum/defcs draw-2 < rum/reactive - {:init (fn [state] - (let [repo (storage/get :git/current-repo)] - - (let [current-title (get-last-title repo)] - (reset! *current-title current-title)) - (let [current-file (or - (get-in (first (:rum/args state)) - [:query-params :file]) - (get-last-file repo))] - (reset! *current-file current-file) - (reset! *current-title (get-file-title current-file)))) - - (if (loaded?) - (set-excalidraw-component!) - (loader/load - (config/asset-uri "/static/js/excalidraw.min.js") - (fn [] - (reset! *loaded? true) - (set-excalidraw-component!)))) - - (draw/get-all-excalidraw-files - (fn [files] - (reset! *files (distinct files)))) - - (state/set-draw! true) - state) - :will-unmount (fn [state] - (state/set-draw! false) - state)} - [state option] - (let [loaded? (or (loaded?) - (rum/react *loaded?)) - current-repo (state/sub :git/current-repo) - component (rum/react *excalidraw)] - (if component - (let [current-file (rum/react *current-file) - current-file (or current-file - (and current-repo - (get-last-file current-repo)))] - (let [key (if current-repo - (str current-repo "-" - (or (and current-file (str "draw-" current-file)) - "draw-with-no-file")) - "draw-with-no-file")] - (rum/with-key (draw-inner option) key))) - [:div.center svg/loading]))) - -(rum/defc draw < rum/reactive - [option] - (let [db-restoring? (state/sub :db/restoring?)] - (if db-restoring? - [:div.ls-center - (ui/loading "Loading")] - (draw-2 option)))) diff --git a/src/main/frontend/components/draw.css b/src/main/frontend/components/draw.css deleted file mode 100644 index bb62ea316..000000000 --- a/src/main/frontend/components/draw.css +++ /dev/null @@ -1,23 +0,0 @@ -#draw { - -webkit-app-region: no-drag; - overflow: hidden; -} - -#draw iframe { - width: 100%; - height: 100%; - border: none; -} - -.draw { - display: flex; - position: fixed; - top: 0; - bottom: 0; - left: 0; - right: 0; -} - -.excalidraw-embed .draw { - position: relative; -} \ No newline at end of file diff --git a/src/main/frontend/components/sidebar.cljs b/src/main/frontend/components/sidebar.cljs index 3f09dd9c3..d81bade7d 100644 --- a/src/main/frontend/components/sidebar.cljs +++ b/src/main/frontend/components/sidebar.cljs @@ -131,7 +131,7 @@ (ui/loading (t :loading))]] :else - [:div {:style {:margin-bottom (if global-graph-pages? 0 120)}} + [:div.max-w-7xl.mx-auto {:style {:margin-bottom (if global-graph-pages? 0 120)}} main-content])]] (right-sidebar/sidebar)])) @@ -176,7 +176,7 @@ preferred-format (state/sub [:me :preferred_format]) logged? (:name me)] (rum/with-context [[t] i18n/*tongue-context*] - [:div.max-w-7xl.mx-auto + [:div (cond (and default-home (= :home (state/get-current-route)) diff --git a/src/main/frontend/encrypt.cljs b/src/main/frontend/encrypt.cljs index 5fef0a5c7..b5be16315 100644 --- a/src/main/frontend/encrypt.cljs +++ b/src/main/frontend/encrypt.cljs @@ -14,8 +14,9 @@ (defn content-encrypted? [content] - (or (str/starts-with? content age-pem-header-line) - (str/starts-with? content age-version-line))) + (when content + (or (str/starts-with? content age-pem-header-line) + (str/starts-with? content age-version-line)))) (defn encrypted-db? [repo-url] @@ -100,4 +101,4 @@ lazy-decrypt-with-user-passphrase (resolve 'frontend.extensions.age-encryption/decrypt-with-user-passphrase) content (utf8/encode content) decrypted (lazy-decrypt-with-user-passphrase passphrase content)] - (utf8/decode decrypted))) \ No newline at end of file + (utf8/decode decrypted))) diff --git a/src/main/frontend/extensions/excalidraw.cljs b/src/main/frontend/extensions/excalidraw.cljs new file mode 100644 index 000000000..8321b1021 --- /dev/null +++ b/src/main/frontend/extensions/excalidraw.cljs @@ -0,0 +1,136 @@ +(ns frontend.extensions.excalidraw + (:require [rum.core :as rum] + [goog.object :as gobj] + [frontend.rum :as r] + [frontend.util :as util :refer-macros [profile]] + [frontend.mixins :as mixins] + [frontend.storage :as storage] + [frontend.components.svg :as svg] + [cljs-bean.core :as bean] + [dommy.core :as d] + [clojure.string :as string] + [frontend.handler.notification :as notification] + [frontend.handler.draw :as draw] + [frontend.handler.file :as file] + [frontend.handler.ui :as ui-handler] + [frontend.ui :as ui] + [frontend.loader :as loader] + [frontend.config :as config] + [frontend.state :as state] + [frontend.search :as search] + [frontend.components.repo :as repo] + [promesa.core :as p] + [reitit.frontend.easy :as rfe] + ["@excalidraw/excalidraw" :as Excalidraw])) + +(def excalidraw (r/adapt-class (gobj/get Excalidraw "default"))) + +(defn from-json + [text] + (when-not (string/blank? text) + (try + (js/JSON.parse text) + (catch js/Error e + (println "from json error:") + (js/console.dir e) + (notification/show! + (util/format "Could not load this invalid excalidraw file") + :error))))) + +(defonce *bounding-width (atom nil)) +(defn- get-bounding-width + [ref] + (when ref + (when-let [current (gobj/get ref "current")] + (-> current + (.getBoundingClientRect) + (gobj/get "width"))))) + +(rum/defcs draw-inner < rum/reactive + (rum/local true ::zen-mode?) + (rum/local false ::view-mode?) + (rum/local nil ::elements) + [state data option] + (let [current-repo (state/sub :git/current-repo) + bounding-width (rum/react *bounding-width) + *zen-mode? (get state ::zen-mode?) + *view-mode? (get state ::view-mode?) + wide-mode? (state/sub :ui/wide-mode?) + *elements (get state ::elements) + file (:file option)] + (when data + [:div.overflow-hidden + [:div.my-1 {:style {:font-size 10}} + [:a.mr-2 {:on-click ui-handler/toggle-wide-mode!} + (util/format "Wide Mode (%s)" (if wide-mode? "ON" "OFF"))] + [:a.mr-2 {:on-click #(swap! *zen-mode? not)} + (util/format "Zen Mode (%s)" (if @*zen-mode? "ON" "OFF"))] + [:a.mr-2 {:on-click #(swap! *view-mode? not)} + (util/format "View Mode (%s)" (if @*view-mode? "ON" "OFF"))]] + [:div + (excalidraw + (merge + {:on-change (fn [elements state] + (let [elements->clj (bean/->clj elements)] + (when (and (seq elements->clj) + (not= elements @*elements)) + (let [state (bean/->clj state)] + (draw/save-excalidraw! + file + (-> {:type "excalidraw" + :version 2 + :source config/website + :elements elements + :appState (select-keys state [:gridSize :viewBackgroundColor])} + bean/->js + (js/JSON.stringify))) + (reset! *elements elements))))) + :zen-mode-enabled @*zen-mode? + :view-mode-enabled @*view-mode? + :grid-mode-enabled false + :initial-data data} + (if wide-mode? + {:height 650} + {:width 800 + :height 500})))]]))) + +(rum/defcs draw-container < rum/reactive + {:init (fn [state] + (let [[option] (:rum/args state) + file (:file option) + *data (atom nil) + *loading? (atom true)] + (when file + (draw/load-excalidraw-file + file + (fn [data] + (let [data (from-json data)] + (reset! *data data) + (reset! *loading? false))))) + (assoc state + ::data *data + ::loading? *loading?)))} + [state option] + (let [*data (get state ::data) + *loading? (get state ::loading?) + loading? (rum/react *loading?) + data (rum/react *data) + db-restoring? (state/sub :db/restoring?)] + (when (:file option) + (cond + db-restoring? + [:div.ls-center + (ui/loading "Loading")] + + (false? loading?) + (draw-inner data option) + + :else ; loading + nil)))) + +(rum/defc draw < rum/reactive + [option] + (let [repo (state/get-current-repo) + granted? (state/sub [:nfs/user-granted? repo])] + (when-not (and (config/local-db? repo) (not granted?)) + (draw-container option)))) diff --git a/src/main/frontend/fs/nfs.cljs b/src/main/frontend/fs/nfs.cljs index d44b5ef40..8749751ff 100644 --- a/src/main/frontend/fs/nfs.cljs +++ b/src/main/frontend/fs/nfs.cljs @@ -133,11 +133,13 @@ not-changed? (= last-modified-at local-last-modified-at) format (-> (util/get-file-ext path) (config/get-file-format)) - pending-writes (state/get-write-chan-length)] + pending-writes (state/get-write-chan-length) + draw? (and path (string/ends-with? path ".excalidraw"))] (if (and local-content (or old-content ;; temporally fix - (and path (string/ends-with? path ".excalidraw"))) new? + draw?) new? (or + draw? ;; Writing not finished (> pending-writes 0) ;; not changed by other editors diff --git a/src/main/frontend/handler/draw.cljs b/src/main/frontend/handler/draw.cljs index 3e23d1d33..ba333b199 100644 --- a/src/main/frontend/handler/draw.cljs +++ b/src/main/frontend/handler/draw.cljs @@ -14,37 +14,6 @@ [cljs-time.core :as t] [cljs-time.coerce :as tc])) -;; state -(defonce *files (atom nil)) -(defonce *current-file (atom nil)) -(defonce *current-title (atom "")) -(defonce *file-loading? (atom nil)) -(defonce *elements (atom nil)) -(defonce *unsaved? (atom false)) -(defonce *search-files (atom [])) -(defonce *saving-title (atom nil)) -(defonce *excalidraw (atom nil)) - -;; TODO: refactor -(defonce draw-state :draw-state) - -(defn get-draw-state [] - (storage/get draw-state)) -(defn set-draw-state! [value] - (storage/set draw-state value)) - -(defn set-k - [k v] - (when-let [repo (state/get-current-repo)] - (let [state (get-draw-state)] - (let [new-state (assoc-in state [repo k] v)] - (set-draw-state! new-state))))) - -(defn set-last-file! - [value] - (set-k :last-file value)) - -;; excalidraw (defn create-draws-directory! [repo] (when repo @@ -55,8 +24,8 @@ (fn [_error] nil))))) (defn save-excalidraw! - [file data ok-handler] - (let [path (str config/default-draw-directory "/" file) + [file data] + (let [path file repo (state/get-current-repo)] (when repo (let [repo-dir (config/get-repo-dir repo)] @@ -65,7 +34,6 @@ (create-draws-directory! repo) (fs/write-file! repo repo-dir path data nil) (git-handler/git-add repo path) - (ok-handler file) (db/transact! repo [{:file/path path :page/name file @@ -75,58 +43,30 @@ (prn "Write file failed, path: " path ", data: " data) (js/console.dir error)))))))) -(defn get-all-excalidraw-files - [ok-handler] - (when-let [repo (state/get-current-repo)] - (p/let [_ (create-draws-directory! repo)] - (let [dir (str (config/get-repo-dir repo) - "/" - config/default-draw-directory)] - (util/p-handle - (fs/readdir dir) - (fn [files] - (let [files (-> (filter #(string/ends-with? % ".excalidraw") files) - (distinct) - (sort) - (reverse))] - (ok-handler files))) - (fn [error] - (js/console.dir error))))))) - (defn load-excalidraw-file [file ok-handler] (when-let [repo (state/get-current-repo)] (util/p-handle - (file-handler/load-file repo (str config/default-draw-directory "/" file)) + (file-handler/load-file repo file) (fn [content] (ok-handler content)) (fn [error] - (prn "Error loading " file ": " - error))))) + (println "Error loading " file ": " + error))))) (defonce default-content (util/format "{\n \"type\": \"excalidraw\",\n \"version\": 2,\n \"source\": \"%s\",\n \"elements\": [],\n \"appState\": {\n \"viewBackgroundColor\": \"#FFF\",\n \"gridSize\": null\n }\n}" config/website)) -(defn title->file-name - [title] - (when (not (string/blank? title)) - (let [title (string/lower-case (string/replace title " " "-"))] - (str (date/get-date-time-string-2) "-" title ".excalidraw")))) +(defn file-name + [] + (str (date/get-date-time-string-2) ".excalidraw")) (defn create-draw-with-default-content - [current-file ok-handler] + [current-file] (when-let [repo (state/get-current-repo)] (p/let [exists? (fs/file-exists? (config/get-repo-dir repo) (str config/default-draw-directory current-file))] (when-not exists? - (save-excalidraw! current-file default-content - (fn [file] - (reset! *files - (distinct (conj @*files file))) - (reset! *current-file file) - (reset! *unsaved? false) - (set-last-file! file) - (reset! *saving-title nil) - (ok-handler))))))) + (save-excalidraw! current-file default-content))))) diff --git a/src/main/frontend/handler/editor.cljs b/src/main/frontend/handler/editor.cljs index 222b33375..f9594b3b3 100644 --- a/src/main/frontend/handler/editor.cljs +++ b/src/main/frontend/handler/editor.cljs @@ -10,7 +10,6 @@ [frontend.handler.repo :as repo-handler] [frontend.handler.file :as file-handler] [frontend.handler.notification :as notification] - [frontend.handler.draw :as draw] [frontend.handler.expand :as expand] [frontend.handler.block :as block-handler] [frontend.format.mldoc :as mldoc] @@ -2112,22 +2111,6 @@ (get-link format link label) format {:last-pattern (str commands/slash "link")}))) - :draw - (when-not (string/blank? (:title m)) - (let [file (draw/title->file-name (:title m)) - value (util/format - "[[%s]]\n" - file - file)] - (insert-command! id - value - format - {:last-pattern (str commands/slash "draw ")}) - (draw/create-draw-with-default-content - file - (fn [] - (let [input (gdom/getElement "download")] - (.click input)))))) nil) (state/set-editor-show-input! nil) diff --git a/src/main/frontend/handler/ui.cljs b/src/main/frontend/handler/ui.cljs index 161f6c2e0..5dd9a2db8 100644 --- a/src/main/frontend/handler/ui.cljs +++ b/src/main/frontend/handler/ui.cljs @@ -92,3 +92,12 @@ ;; (state/get-custom-css-link) )] (util/add-style! style))) + +(defn toggle-wide-mode! + [] + (let [wide? (state/get-wide-mode?) + elements (array-seq (js/document.getElementsByClassName "cp__sidebar-main-content")) + max-width (if wide? "var(--ls-main-content-max-width)" "100%")] + (when-let [element (first elements)] + (dom/set-style! element :max-width max-width)) + (state/toggle-wide-mode!))) diff --git a/src/main/frontend/keyboards.cljs b/src/main/frontend/keyboards.cljs index 152f65ef2..793804811 100644 --- a/src/main/frontend/keyboards.cljs +++ b/src/main/frontend/keyboards.cljs @@ -51,6 +51,8 @@ (enable-when-not-editing-mode! ui-handler/toggle-contents!) (or (shortcut :editor/toggle-settings) "t s") (enable-when-not-editing-mode! ui-handler/toggle-settings-modal!) + (or (shortcut :ui/toggle-wide-mode) "t w") + (enable-when-not-editing-mode! ui-handler/toggle-wide-mode!) (or (shortcut :ui/toggle-between-page-and-file) "s") (enable-when-not-editing-mode! route-handler/toggle-between-page-and-file!) (or (shortcut :git/commit) "c") diff --git a/src/main/frontend/routes.cljs b/src/main/frontend/routes.cljs index 9a598f043..640f0d288 100644 --- a/src/main/frontend/routes.cljs +++ b/src/main/frontend/routes.cljs @@ -4,7 +4,6 @@ [frontend.components.file :as file] [frontend.components.page :as page] [frontend.components.diff :as diff] - [frontend.components.draw :as draw] [frontend.components.journal :as journal] [frontend.components.settings :as settings] [frontend.components.external :as external] @@ -52,10 +51,6 @@ {:name :diff :view diff/diff}] - ["/draw" - {:name :draw - :view draw/draw}] - ["/settings" {:name :settings :view settings/settings}] diff --git a/src/main/frontend/state.cljs b/src/main/frontend/state.cljs index 7f18c7923..3dbece8d7 100644 --- a/src/main/frontend/state.cljs +++ b/src/main/frontend/state.cljs @@ -54,6 +54,7 @@ :ui/sidebar-open? false :ui/left-sidebar-open? false :ui/theme (or (storage/get :ui/theme) "dark") + :ui/wide-mode? false ;; :show-all, :hide-block-body, :hide-block-children :ui/cycle-collapse :show-all :ui/collapsed-blocks {} @@ -899,10 +900,13 @@ (set-state! :indexeddb/support? value)) (defn set-modal! - [modal-panel-content] - (swap! state assoc - :modal/show? (boolean modal-panel-content) - :modal/panel-content modal-panel-content)) + ([modal-panel-content] + (set-modal! modal-panel-content false)) + ([modal-panel-content fullscreen?] + (swap! state assoc + :modal/show? (boolean modal-panel-content) + :modal/panel-content modal-panel-content + :modal/fullscreen? fullscreen?))) (defn close-modal! [] @@ -992,6 +996,14 @@ [] (get-in @state [:repo/changed-files (get-current-repo)])) +(defn get-wide-mode? + [] + (:ui/wide-mode? @state)) + +(defn toggle-wide-mode! + [] + (update-state! :ui/wide-mode? not)) + (defn set-online! [value] (set-state! :network/online? value)) diff --git a/src/main/frontend/ui.cljs b/src/main/frontend/ui.cljs index d218d1d5f..ff289b20b 100644 --- a/src/main/frontend/ui.cljs +++ b/src/main/frontend/ui.cljs @@ -424,7 +424,7 @@ [:div.absolute.inset-0.opacity-75]]) (rum/defc modal-panel - [panel-content transition-state close-fn] + [panel-content transition-state close-fn fullscreen?] [:div.ui__modal-panel.transform.transition-all.sm:min-w-lg.sm {:class (case transition-state "entering" "ease-out duration-300 opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" @@ -444,7 +444,7 @@ :stroke-linejoin "round" :stroke-linecap "round"}]]]] - [:div.panel-content + [:div {:class (if fullscreen? "" "panel-content")} (panel-content close-fn)]]) (rum/defc modal < rum/reactive @@ -458,6 +458,7 @@ :outside? false))) [] (let [modal-panel-content (state/sub :modal/panel-content) + fullscreen? (state/sub :modal/fullscreen?) show? (boolean modal-panel-content) close-fn #(state/close-modal!) modal-panel-content (or modal-panel-content (fn [close] [:div]))] @@ -470,7 +471,7 @@ (css-transition {:in show? :timeout 0} (fn [state] - (modal-panel modal-panel-content state close-fn)))])) + (modal-panel modal-panel-content state close-fn fullscreen?)))])) (defn make-confirm-modal [{:keys [tag title sub-title sub-checkbox? on-cancel on-confirm] :as opts}]