diff --git a/bin/pbpaste-img b/bin/pbpaste-img new file mode 100755 index 0000000..ed7b51e --- /dev/null +++ b/bin/pbpaste-img @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# +# Save the current Windows clipboard image into WSL and copy its WSL path. + +set -Eeuo pipefail + +script_file="$(readlink -f -- "${BASH_SOURCE[0]}")" +script_dir="$(cd -- "$(dirname -- "$script_file")" && pwd -P)" +out_root="${PBPASTE_IMG_DIR:-$HOME/.local/pbpaste-img}" +script_path="$script_dir/pbpaste-img.ps1" +bashlib_path="$script_dir/../zsh/bashlib.sh" + +if ! command -v powershell.exe >/dev/null 2>&1; then + echo "ERROR: powershell.exe is required for Windows clipboard access." >&2 + exit 1 +fi + +if ! command -v wslpath >/dev/null 2>&1; then + echo "ERROR: wslpath is required to bridge WSL and Windows paths." >&2 + exit 1 +fi + +mkdir -p "$out_root" + +script_path_windows="$(wslpath -w "$script_path")" +out_root_windows="$(wslpath -w "$out_root")" + +if [[ -f "$bashlib_path" ]]; then + # shellcheck source=/dev/null + source "$bashlib_path" +fi + +powershell_args=( + -NoLogo + -NoProfile + -ExecutionPolicy Bypass + -STA + -File "$script_path_windows" + -OutRootWindows "$out_root_windows" +) + +if declare -F is_wsl >/dev/null 2>&1 && is_wsl >/dev/null; then + powershell_args+=(-OutRootWsl "$out_root") +else + powershell_args+=(-WindowsPathToClipboard) +fi + +powershell.exe "${powershell_args[@]}" "$@" | tr -d '\r' diff --git a/bin/pbpaste-img.ps1 b/bin/pbpaste-img.ps1 new file mode 100644 index 0000000..9d44144 --- /dev/null +++ b/bin/pbpaste-img.ps1 @@ -0,0 +1,97 @@ +[CmdletBinding()] +param( + [string]$OutRootWindows, + [string]$OutRootWsl = $env:PBPASTE_IMG_ROOT_WSL, + [switch]$WindowsPathToClipboard +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +Add-Type -AssemblyName System.Windows.Forms +Add-Type -AssemblyName System.Drawing + +function Invoke-WslText { + param([string[]]$Arguments) + + $output = & wsl.exe @Arguments + if ($LASTEXITCODE -ne 0) { + throw "wsl.exe $($Arguments -join ' ') failed with exit code $LASTEXITCODE" + } + + return (($output -join "`n").Trim()) +} + +function Invoke-WslChecked { + param([string[]]$Arguments) + + & wsl.exe @Arguments | Out-Null + if ($LASTEXITCODE -ne 0) { + throw "wsl.exe $($Arguments -join ' ') failed with exit code $LASTEXITCODE" + } +} + +function Join-WslPath { + param( + [string]$Root, + [string]$Child + ) + + return (($Root.TrimEnd("/", "\")) + "/" + $Child) +} + +if ([string]::IsNullOrWhiteSpace($OutRootWsl) -and + [string]::IsNullOrWhiteSpace($OutRootWindows)) { + $wslHome = Invoke-WslText -Arguments @("sh", "-lc", 'printf %s "$HOME"') + $OutRootWsl = Join-WslPath -Root $wslHome -Child ".local/pbpaste-img" +} + +$month = Get-Date -Format "yy-MM" +$timestamp = Get-Date -Format "yyyyMMdd-HHmmss" +$fileName = "$timestamp.png" + +if (-not [string]::IsNullOrWhiteSpace($OutRootWsl)) { + $outDirWsl = Join-WslPath -Root $OutRootWsl -Child $month + Invoke-WslChecked -Arguments @("mkdir", "-p", $outDirWsl) +} else { + $outDirWsl = $null +} + +if ([string]::IsNullOrWhiteSpace($OutRootWindows)) { + if ([string]::IsNullOrWhiteSpace($outDirWsl)) { + throw "Either -OutRootWindows or -OutRootWsl must be set." + } + $outDirWindows = Invoke-WslText -Arguments @("wslpath", "-w", $outDirWsl) +} else { + $outDirWindows = Join-Path $OutRootWindows $month + New-Item -ItemType Directory -Force -Path $outDirWindows | Out-Null +} + +$outFileWindows = Join-Path $outDirWindows $fileName + +if (-not [string]::IsNullOrWhiteSpace($outDirWsl)) { + $outFileWsl = Join-WslPath -Root $outDirWsl -Child $fileName +} else { + $outFileWsl = $null +} + +$image = [System.Windows.Forms.Clipboard]::GetImage() +if ($null -eq $image) { + Write-Error "No bitmap image found on the clipboard." + exit 1 +} + +try { + $image.Save($outFileWindows, [System.Drawing.Imaging.ImageFormat]::Png) +} finally { + $image.Dispose() +} + +if ($WindowsPathToClipboard -or [string]::IsNullOrWhiteSpace($outFileWsl)) { + $clipboardPath = $outFileWindows +} else { + $clipboardPath = $outFileWsl +} + +Set-Clipboard -Value $clipboardPath +Write-Output $clipboardPath diff --git a/css/README.md b/css/README.md new file mode 100644 index 0000000..1877f24 --- /dev/null +++ b/css/README.md @@ -0,0 +1,29 @@ +# CSS Assets + +## `github-markdown.min.css` + +Downloaded from Cloudflare cdnjs for local Pandoc/Markdown previews. + +- Package: `github-markdown-css` +- Version: `5.8.1` +- Source URL: `https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.8.1/github-markdown.min.css` +- Local path: `$DOTFILES/css/github-markdown.min.css` + +## `github-markdown-pandoc.css` + +Small local override for Pandoc-generated HTML. The upstream +`github-markdown-css` package expects rendered Markdown to live inside +`.markdown-body`, but it does not set the GitHub-like page width/padding by +itself. + +- Local path: `$DOTFILES/css/github-markdown-pandoc.css` +- Adds GitHub's typical `max-width: 980px`, centered layout, and `45px` page + padding for `.markdown-body`. + +Example usage: + +```bash +GH_MD_CSS="$DOTFILES/css/github-markdown.min.css" +GH_PANDOC_CSS="$DOTFILES/css/github-markdown-pandoc.css" +auto-pandoc calc-liq.md -s --katex --css="$GH_MD_CSS" --css="$GH_PANDOC_CSS" -o index.html +``` diff --git a/css/github-markdown-pandoc.css b/css/github-markdown-pandoc.css new file mode 100644 index 0000000..26eae60 --- /dev/null +++ b/css/github-markdown-pandoc.css @@ -0,0 +1,19 @@ +.markdown-body { + box-sizing: border-box; + min-width: 200px; + max-width: 980px; + margin: 0 auto; + padding: 45px; +} + +@media (max-width: 767px) { + .markdown-body { + padding: 15px; + } +} + +@media (prefers-color-scheme: dark) { + body { + background-color: #0d1117; + } +} diff --git a/css/github-markdown.min.css b/css/github-markdown.min.css new file mode 100644 index 0000000..f9f1a28 --- /dev/null +++ b/css/github-markdown.min.css @@ -0,0 +1 @@ +.markdown-body{--base-size-4:0.25rem;--base-size-8:0.5rem;--base-size-16:1rem;--base-size-24:1.5rem;--base-size-40:2.5rem;--base-text-weight-normal:400;--base-text-weight-medium:500;--base-text-weight-semibold:600;--fontStack-monospace:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;--fgColor-accent:Highlight}@media (prefers-color-scheme:dark){.markdown-body,[data-theme=dark]{color-scheme:dark;--focus-outlineColor:#1f6feb;--fgColor-default:#f0f6fc;--fgColor-muted:#9198a1;--fgColor-accent:#4493f8;--fgColor-success:#3fb950;--fgColor-attention:#d29922;--fgColor-danger:#f85149;--fgColor-done:#ab7df8;--bgColor-default:#0d1117;--bgColor-muted:#151b23;--bgColor-neutral-muted:#656c7633;--bgColor-attention-muted:#bb800926;--borderColor-default:#3d444d;--borderColor-muted:#3d444db3;--borderColor-neutral-muted:#3d444db3;--borderColor-accent-emphasis:#1f6feb;--borderColor-success-emphasis:#238636;--borderColor-attention-emphasis:#9e6a03;--borderColor-danger-emphasis:#da3633;--borderColor-done-emphasis:#8957e5;--color-prettylights-syntax-comment:#9198a1;--color-prettylights-syntax-constant:#79c0ff;--color-prettylights-syntax-constant-other-reference-link:#a5d6ff;--color-prettylights-syntax-entity:#d2a8ff;--color-prettylights-syntax-storage-modifier-import:#f0f6fc;--color-prettylights-syntax-entity-tag:#7ee787;--color-prettylights-syntax-keyword:#ff7b72;--color-prettylights-syntax-string:#a5d6ff;--color-prettylights-syntax-variable:#ffa657;--color-prettylights-syntax-brackethighlighter-unmatched:#f85149;--color-prettylights-syntax-brackethighlighter-angle:#9198a1;--color-prettylights-syntax-invalid-illegal-text:#f0f6fc;--color-prettylights-syntax-invalid-illegal-bg:#8e1519;--color-prettylights-syntax-carriage-return-text:#f0f6fc;--color-prettylights-syntax-carriage-return-bg:#b62324;--color-prettylights-syntax-string-regexp:#7ee787;--color-prettylights-syntax-markup-list:#f2cc60;--color-prettylights-syntax-markup-heading:#1f6feb;--color-prettylights-syntax-markup-italic:#f0f6fc;--color-prettylights-syntax-markup-bold:#f0f6fc;--color-prettylights-syntax-markup-deleted-text:#ffdcd7;--color-prettylights-syntax-markup-deleted-bg:#67060c;--color-prettylights-syntax-markup-inserted-text:#aff5b4;--color-prettylights-syntax-markup-inserted-bg:#033a16;--color-prettylights-syntax-markup-changed-text:#ffdfb6;--color-prettylights-syntax-markup-changed-bg:#5a1e02;--color-prettylights-syntax-markup-ignored-text:#f0f6fc;--color-prettylights-syntax-markup-ignored-bg:#1158c7;--color-prettylights-syntax-meta-diff-range:#d2a8ff;--color-prettylights-syntax-sublimelinter-gutter-mark:#3d444d}}@media (prefers-color-scheme:light){.markdown-body,[data-theme=light]{color-scheme:light;--focus-outlineColor:#0969da;--fgColor-default:#1f2328;--fgColor-muted:#59636e;--fgColor-accent:#0969da;--fgColor-success:#1a7f37;--fgColor-attention:#9a6700;--fgColor-danger:#d1242f;--fgColor-done:#8250df;--bgColor-default:#ffffff;--bgColor-muted:#f6f8fa;--bgColor-neutral-muted:#818b981f;--bgColor-attention-muted:#fff8c5;--borderColor-default:#d1d9e0;--borderColor-muted:#d1d9e0b3;--borderColor-neutral-muted:#d1d9e0b3;--borderColor-accent-emphasis:#0969da;--borderColor-success-emphasis:#1a7f37;--borderColor-attention-emphasis:#9a6700;--borderColor-danger-emphasis:#cf222e;--borderColor-done-emphasis:#8250df;--color-prettylights-syntax-comment:#59636e;--color-prettylights-syntax-constant:#0550ae;--color-prettylights-syntax-constant-other-reference-link:#0a3069;--color-prettylights-syntax-entity:#6639ba;--color-prettylights-syntax-storage-modifier-import:#1f2328;--color-prettylights-syntax-entity-tag:#0550ae;--color-prettylights-syntax-keyword:#cf222e;--color-prettylights-syntax-string:#0a3069;--color-prettylights-syntax-variable:#953800;--color-prettylights-syntax-brackethighlighter-unmatched:#82071e;--color-prettylights-syntax-brackethighlighter-angle:#59636e;--color-prettylights-syntax-invalid-illegal-text:#f6f8fa;--color-prettylights-syntax-invalid-illegal-bg:#82071e;--color-prettylights-syntax-carriage-return-text:#f6f8fa;--color-prettylights-syntax-carriage-return-bg:#cf222e;--color-prettylights-syntax-string-regexp:#116329;--color-prettylights-syntax-markup-list:#3b2300;--color-prettylights-syntax-markup-heading:#0550ae;--color-prettylights-syntax-markup-italic:#1f2328;--color-prettylights-syntax-markup-bold:#1f2328;--color-prettylights-syntax-markup-deleted-text:#82071e;--color-prettylights-syntax-markup-deleted-bg:#ffebe9;--color-prettylights-syntax-markup-inserted-text:#116329;--color-prettylights-syntax-markup-inserted-bg:#dafbe1;--color-prettylights-syntax-markup-changed-text:#953800;--color-prettylights-syntax-markup-changed-bg:#ffd8b5;--color-prettylights-syntax-markup-ignored-text:#d1d9e0;--color-prettylights-syntax-markup-ignored-bg:#0550ae;--color-prettylights-syntax-meta-diff-range:#8250df;--color-prettylights-syntax-sublimelinter-gutter-mark:#818b98}}.markdown-body{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;margin:0;color:var(--fgColor-default);background-color:var(--bgColor-default);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body .octicon{display:inline-block;fill:currentColor;vertical-align:text-bottom}.markdown-body h1:hover .anchor .octicon-link:before,.markdown-body h2:hover .anchor .octicon-link:before,.markdown-body h3:hover .anchor .octicon-link:before,.markdown-body h4:hover .anchor .octicon-link:before,.markdown-body h5:hover .anchor .octicon-link:before,.markdown-body h6:hover .anchor .octicon-link:before{width:16px;height:16px;content:' ';display:inline-block;background-color:currentColor;-webkit-mask-image:url("data:image/svg+xml,");mask-image:url("data:image/svg+xml,")}.markdown-body details,.markdown-body figcaption,.markdown-body figure{display:block}.markdown-body summary{display:list-item}.markdown-body [hidden]{display:none!important}.markdown-body a{background-color:transparent;color:var(--fgColor-accent);text-decoration:none}.markdown-body abbr[title]{border-bottom:none;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.markdown-body b,.markdown-body strong{font-weight:var(--base-text-weight-semibold,600)}.markdown-body dfn{font-style:italic}.markdown-body h1{margin:.67em 0;font-weight:var(--base-text-weight-semibold,600);padding-bottom:.3em;font-size:2em;border-bottom:1px solid var(--borderColor-muted)}.markdown-body mark{background-color:var(--bgColor-attention-muted);color:var(--fgColor-default)}.markdown-body small{font-size:90%}.markdown-body sub,.markdown-body sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.markdown-body sub{bottom:-.25em}.markdown-body sup{top:-.5em}.markdown-body img{border-style:none;max-width:100%;box-sizing:content-box}.markdown-body code,.markdown-body kbd,.markdown-body pre,.markdown-body samp{font-family:monospace;font-size:1em}.markdown-body figure{margin:1em var(--base-size-40)}.markdown-body hr{box-sizing:content-box;overflow:hidden;background:0 0;border-bottom:1px solid var(--borderColor-muted);height:.25em;padding:0;margin:var(--base-size-24) 0;background-color:var(--borderColor-default);border:0}.markdown-body input{font:inherit;margin:0;overflow:visible;font-family:inherit;font-size:inherit;line-height:inherit}.markdown-body [type=button],.markdown-body [type=reset],.markdown-body [type=submit]{-webkit-appearance:button;appearance:button}.markdown-body [type=checkbox],.markdown-body [type=radio]{box-sizing:border-box;padding:0}.markdown-body [type=number]::-webkit-inner-spin-button,.markdown-body [type=number]::-webkit-outer-spin-button{height:auto}.markdown-body [type=search]::-webkit-search-cancel-button,.markdown-body [type=search]::-webkit-search-decoration{-webkit-appearance:none;appearance:none}.markdown-body ::-webkit-input-placeholder{color:inherit;opacity:.54}.markdown-body ::-webkit-file-upload-button{-webkit-appearance:button;appearance:button;font:inherit}.markdown-body a:hover{text-decoration:underline}.markdown-body ::placeholder{color:var(--fgColor-muted);opacity:1}.markdown-body hr::before{display:table;content:""}.markdown-body hr::after{display:table;clear:both;content:""}.markdown-body table{border-spacing:0;border-collapse:collapse;display:block;width:max-content;max-width:100%;overflow:auto;font-variant:tabular-nums}.markdown-body td,.markdown-body th{padding:0}.markdown-body details summary{cursor:pointer}.markdown-body [role=button]:focus,.markdown-body a:focus,.markdown-body input[type=checkbox]:focus,.markdown-body input[type=radio]:focus{outline:2px solid var(--focus-outlineColor);outline-offset:-2px;box-shadow:none}.markdown-body [role=button]:focus:not(:focus-visible),.markdown-body a:focus:not(:focus-visible),.markdown-body input[type=checkbox]:focus:not(:focus-visible),.markdown-body input[type=radio]:focus:not(:focus-visible){outline:solid 1px transparent}.markdown-body [role=button]:focus-visible,.markdown-body a:focus-visible,.markdown-body input[type=checkbox]:focus-visible,.markdown-body input[type=radio]:focus-visible{outline:2px solid var(--focus-outlineColor);outline-offset:-2px;box-shadow:none}.markdown-body a:not([class]):focus,.markdown-body a:not([class]):focus-visible,.markdown-body input[type=checkbox]:focus,.markdown-body input[type=checkbox]:focus-visible,.markdown-body input[type=radio]:focus,.markdown-body input[type=radio]:focus-visible{outline-offset:0}.markdown-body kbd{display:inline-block;padding:var(--base-size-4);font:11px var(--fontStack-monospace,ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace);line-height:10px;color:var(--fgColor-default);vertical-align:middle;background-color:var(--bgColor-muted);border:solid 1px var(--borderColor-neutral-muted);border-bottom-color:var(--borderColor-neutral-muted);border-radius:6px;box-shadow:inset 0 -1px 0 var(--borderColor-neutral-muted)}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:var(--base-size-24);margin-bottom:var(--base-size-16);font-weight:var(--base-text-weight-semibold,600);line-height:1.25}.markdown-body h2{font-weight:var(--base-text-weight-semibold,600);padding-bottom:.3em;font-size:1.5em;border-bottom:1px solid var(--borderColor-muted)}.markdown-body h3{font-weight:var(--base-text-weight-semibold,600);font-size:1.25em}.markdown-body h4{font-weight:var(--base-text-weight-semibold,600);font-size:1em}.markdown-body h5{font-weight:var(--base-text-weight-semibold,600);font-size:.875em}.markdown-body h6{font-weight:var(--base-text-weight-semibold,600);font-size:.85em;color:var(--fgColor-muted)}.markdown-body p{margin-top:0;margin-bottom:10px}.markdown-body blockquote{margin:0;padding:0 1em;color:var(--fgColor-muted);border-left:.25em solid var(--borderColor-default)}.markdown-body ol,.markdown-body ul{margin-top:0;margin-bottom:0;padding-left:2em}.markdown-body ol ol,.markdown-body ul ol{list-style-type:lower-roman}.markdown-body ol ol ol,.markdown-body ol ul ol,.markdown-body ul ol ol,.markdown-body ul ul ol{list-style-type:lower-alpha}.markdown-body dd{margin-left:0}.markdown-body code,.markdown-body samp,.markdown-body tt{font-family:var(--fontStack-monospace,ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace);font-size:12px}.markdown-body pre{margin-top:0;margin-bottom:0;font-family:var(--fontStack-monospace,ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace);font-size:12px;word-wrap:normal}.markdown-body .octicon{display:inline-block;overflow:visible!important;vertical-align:text-bottom;fill:currentColor}.markdown-body input::-webkit-inner-spin-button,.markdown-body input::-webkit-outer-spin-button{margin:0;appearance:none}.markdown-body .mr-2{margin-right:var(--base-size-8,8px)!important}.markdown-body::before{display:table;content:""}.markdown-body::after{display:table;clear:both;content:""}.markdown-body>:first-child{margin-top:0!important}.markdown-body>:last-child{margin-bottom:0!important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .absent{color:var(--fgColor-danger)}.markdown-body .anchor{float:left;padding-right:var(--base-size-4);margin-left:-20px;line-height:1}.markdown-body .anchor:focus{outline:0}.markdown-body blockquote,.markdown-body details,.markdown-body dl,.markdown-body ol,.markdown-body p,.markdown-body pre,.markdown-body table,.markdown-body ul{margin-top:0;margin-bottom:var(--base-size-16)}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:var(--fgColor-default);vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1 code,.markdown-body h1 tt,.markdown-body h2 code,.markdown-body h2 tt,.markdown-body h3 code,.markdown-body h3 tt,.markdown-body h4 code,.markdown-body h4 tt,.markdown-body h5 code,.markdown-body h5 tt,.markdown-body h6 code,.markdown-body h6 tt{padding:0 .2em;font-size:inherit}.markdown-body summary h1,.markdown-body summary h2,.markdown-body summary h3,.markdown-body summary h4,.markdown-body summary h5,.markdown-body summary h6{display:inline-block}.markdown-body summary h1 .anchor,.markdown-body summary h2 .anchor,.markdown-body summary h3 .anchor,.markdown-body summary h4 .anchor,.markdown-body summary h5 .anchor,.markdown-body summary h6 .anchor{margin-left:-40px}.markdown-body summary h1,.markdown-body summary h2{padding-bottom:0;border-bottom:0}.markdown-body ol.no-list,.markdown-body ul.no-list{padding:0;list-style-type:none}.markdown-body ol[type="a s"]{list-style-type:lower-alpha}.markdown-body ol[type="A s"]{list-style-type:upper-alpha}.markdown-body ol[type="i s"]{list-style-type:lower-roman}.markdown-body ol[type="I s"]{list-style-type:upper-roman}.markdown-body ol[type="1"]{list-style-type:decimal}.markdown-body div>ol:not([type]){list-style-type:decimal}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:0;margin-bottom:0}.markdown-body li>p{margin-top:var(--base-size-16)}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:var(--base-size-16);font-size:1em;font-style:italic;font-weight:var(--base-text-weight-semibold,600)}.markdown-body dl dd{padding:0 var(--base-size-16);margin-bottom:var(--base-size-16)}.markdown-body table th{font-weight:var(--base-text-weight-semibold,600)}.markdown-body table td,.markdown-body table th{padding:6px 13px;border:1px solid var(--borderColor-default)}.markdown-body table td>:last-child{margin-bottom:0}.markdown-body table tr{background-color:var(--bgColor-default);border-top:1px solid var(--borderColor-muted)}.markdown-body table tr:nth-child(2n){background-color:var(--bgColor-muted)}.markdown-body table img{background-color:transparent}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body .emoji{max-width:none;vertical-align:text-top;background-color:transparent}.markdown-body span.frame{display:block;overflow:hidden}.markdown-body span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid var(--borderColor-default)}.markdown-body span.frame span img{display:block;float:left}.markdown-body span.frame span span{display:block;padding:5px 0 0;clear:both;color:var(--fgColor-default)}.markdown-body span.align-center{display:block;overflow:hidden;clear:both}.markdown-body span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown-body span.align-center span img{margin:0 auto;text-align:center}.markdown-body span.align-right{display:block;overflow:hidden;clear:both}.markdown-body span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown-body span.align-right span img{margin:0;text-align:right}.markdown-body span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown-body span.float-left span{margin:13px 0 0}.markdown-body span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown-body span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown-body code,.markdown-body tt{padding:.2em .4em;margin:0;font-size:85%;white-space:break-spaces;background-color:var(--bgColor-neutral-muted);border-radius:6px}.markdown-body code br,.markdown-body tt br{display:none}.markdown-body del code{text-decoration:inherit}.markdown-body samp{font-size:85%}.markdown-body pre code{font-size:100%}.markdown-body pre>code{padding:0;margin:0;word-break:normal;white-space:pre;background:0 0;border:0}.markdown-body .highlight{margin-bottom:var(--base-size-16)}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{padding:var(--base-size-16);overflow:auto;font-size:85%;line-height:1.45;color:var(--fgColor-default);background-color:var(--bgColor-muted);border-radius:6px}.markdown-body pre code,.markdown-body pre tt{display:inline;max-width:auto;padding:0;margin:0;overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown-body .csv-data td,.markdown-body .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown-body .csv-data .blob-num{padding:10px var(--base-size-8) 9px;text-align:right;background:var(--bgColor-default);border:0}.markdown-body .csv-data tr{border-top:0}.markdown-body .csv-data th{font-weight:var(--base-text-weight-semibold,600);background:var(--bgColor-muted);border-top:0}.markdown-body [data-footnote-ref]::before{content:"["}.markdown-body [data-footnote-ref]::after{content:"]"}.markdown-body .footnotes{font-size:12px;color:var(--fgColor-muted);border-top:1px solid var(--borderColor-default)}.markdown-body .footnotes ol{padding-left:var(--base-size-16)}.markdown-body .footnotes ol ul{display:inline-block;padding-left:var(--base-size-16);margin-top:var(--base-size-16)}.markdown-body .footnotes li{position:relative}.markdown-body .footnotes li:target::before{position:absolute;top:calc(var(--base-size-8)*-1);right:calc(var(--base-size-8)*-1);bottom:calc(var(--base-size-8)*-1);left:calc(var(--base-size-24)*-1);pointer-events:none;content:"";border:2px solid var(--borderColor-accent-emphasis);border-radius:6px}.markdown-body .footnotes li:target{color:var(--fgColor-default)}.markdown-body .footnotes .data-footnote-backref g-emoji{font-family:monospace}.markdown-body body:has(:modal){padding-right:var(--dialog-scrollgutter)!important}.markdown-body .pl-c{color:var(--color-prettylights-syntax-comment)}.markdown-body .pl-c1,.markdown-body .pl-s .pl-v{color:var(--color-prettylights-syntax-constant)}.markdown-body .pl-e,.markdown-body .pl-en{color:var(--color-prettylights-syntax-entity)}.markdown-body .pl-s .pl-s1,.markdown-body .pl-smi{color:var(--color-prettylights-syntax-storage-modifier-import)}.markdown-body .pl-ent{color:var(--color-prettylights-syntax-entity-tag)}.markdown-body .pl-k{color:var(--color-prettylights-syntax-keyword)}.markdown-body .pl-pds,.markdown-body .pl-s,.markdown-body .pl-s .pl-pse .pl-s1,.markdown-body .pl-sr,.markdown-body .pl-sr .pl-cce,.markdown-body .pl-sr .pl-sra,.markdown-body .pl-sr .pl-sre{color:var(--color-prettylights-syntax-string)}.markdown-body .pl-smw,.markdown-body .pl-v{color:var(--color-prettylights-syntax-variable)}.markdown-body .pl-bu{color:var(--color-prettylights-syntax-brackethighlighter-unmatched)}.markdown-body .pl-ii{color:var(--color-prettylights-syntax-invalid-illegal-text);background-color:var(--color-prettylights-syntax-invalid-illegal-bg)}.markdown-body .pl-c2{color:var(--color-prettylights-syntax-carriage-return-text);background-color:var(--color-prettylights-syntax-carriage-return-bg)}.markdown-body .pl-sr .pl-cce{font-weight:700;color:var(--color-prettylights-syntax-string-regexp)}.markdown-body .pl-ml{color:var(--color-prettylights-syntax-markup-list)}.markdown-body .pl-mh,.markdown-body .pl-mh .pl-en,.markdown-body .pl-ms{font-weight:700;color:var(--color-prettylights-syntax-markup-heading)}.markdown-body .pl-mi{font-style:italic;color:var(--color-prettylights-syntax-markup-italic)}.markdown-body .pl-mb{font-weight:700;color:var(--color-prettylights-syntax-markup-bold)}.markdown-body .pl-md{color:var(--color-prettylights-syntax-markup-deleted-text);background-color:var(--color-prettylights-syntax-markup-deleted-bg)}.markdown-body .pl-mi1{color:var(--color-prettylights-syntax-markup-inserted-text);background-color:var(--color-prettylights-syntax-markup-inserted-bg)}.markdown-body .pl-mc{color:var(--color-prettylights-syntax-markup-changed-text);background-color:var(--color-prettylights-syntax-markup-changed-bg)}.markdown-body .pl-mi2{color:var(--color-prettylights-syntax-markup-ignored-text);background-color:var(--color-prettylights-syntax-markup-ignored-bg)}.markdown-body .pl-mdr{font-weight:700;color:var(--color-prettylights-syntax-meta-diff-range)}.markdown-body .pl-ba{color:var(--color-prettylights-syntax-brackethighlighter-angle)}.markdown-body .pl-sg{color:var(--color-prettylights-syntax-sublimelinter-gutter-mark)}.markdown-body .pl-corl{text-decoration:underline;color:var(--color-prettylights-syntax-constant-other-reference-link)}.markdown-body [role=button]:focus:not(:focus-visible),.markdown-body [role=tabpanel][tabindex="0"]:focus:not(:focus-visible),.markdown-body a:focus:not(:focus-visible),.markdown-body button:focus:not(:focus-visible),.markdown-body summary:focus:not(:focus-visible){outline:0;box-shadow:none}.markdown-body [tabindex="0"]:focus:not(:focus-visible),.markdown-body details-dialog:focus:not(:focus-visible){outline:0}.markdown-body g-emoji{display:inline-block;min-width:1ch;font-family:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:1em;font-style:normal!important;font-weight:var(--base-text-weight-normal,400);line-height:1;vertical-align:-.075em}.markdown-body g-emoji img{width:1em;height:1em}.markdown-body .task-list-item{list-style-type:none}.markdown-body .task-list-item label{font-weight:var(--base-text-weight-normal,400)}.markdown-body .task-list-item.enabled label{cursor:pointer}.markdown-body .task-list-item+.task-list-item{margin-top:var(--base-size-4)}.markdown-body .task-list-item .handle{display:none}.markdown-body .task-list-item-checkbox{margin:0 .2em .25em -1.4em;vertical-align:middle}.markdown-body ul:dir(rtl) .task-list-item-checkbox{margin:0 -1.6em .25em .2em}.markdown-body ol:dir(rtl) .task-list-item-checkbox{margin:0 -1.6em .25em .2em}.markdown-body .contains-task-list:focus-within .task-list-item-convert-container,.markdown-body .contains-task-list:hover .task-list-item-convert-container{display:block;width:auto;height:24px;overflow:visible;clip:auto}.markdown-body ::-webkit-calendar-picker-indicator{filter:invert(50%)}.markdown-body .markdown-alert{padding:var(--base-size-8) var(--base-size-16);margin-bottom:var(--base-size-16);color:inherit;border-left:.25em solid var(--borderColor-default)}.markdown-body .markdown-alert>:first-child{margin-top:0}.markdown-body .markdown-alert>:last-child{margin-bottom:0}.markdown-body .markdown-alert .markdown-alert-title{display:flex;font-weight:var(--base-text-weight-medium,500);align-items:center;line-height:1}.markdown-body .markdown-alert.markdown-alert-note{border-left-color:var(--borderColor-accent-emphasis)}.markdown-body .markdown-alert.markdown-alert-note .markdown-alert-title{color:var(--fgColor-accent)}.markdown-body .markdown-alert.markdown-alert-important{border-left-color:var(--borderColor-done-emphasis)}.markdown-body .markdown-alert.markdown-alert-important .markdown-alert-title{color:var(--fgColor-done)}.markdown-body .markdown-alert.markdown-alert-warning{border-left-color:var(--borderColor-attention-emphasis)}.markdown-body .markdown-alert.markdown-alert-warning .markdown-alert-title{color:var(--fgColor-attention)}.markdown-body .markdown-alert.markdown-alert-tip{border-left-color:var(--borderColor-success-emphasis)}.markdown-body .markdown-alert.markdown-alert-tip .markdown-alert-title{color:var(--fgColor-success)}.markdown-body .markdown-alert.markdown-alert-caution{border-left-color:var(--borderColor-danger-emphasis)}.markdown-body .markdown-alert.markdown-alert-caution .markdown-alert-title{color:var(--fgColor-danger)}.markdown-body>:first-child>.heading-element:first-child{margin-top:0!important}.markdown-body .highlight pre:has(+.zeroclipboard-container){min-height:52px} \ No newline at end of file diff --git a/justfile b/justfile index c1c8977..f5da960 100644 --- a/justfile +++ b/justfile @@ -14,7 +14,8 @@ alias t := test # Install baseline Ubuntu/WSL shell dependencies. i-bash: - sudo apt install -y build-essential ripgrep gh libclang-dev wslu + sudo apt install -y build-essential ripgrep gh libclang-dev wslu \ + ca-certificates gnupg curl trash-cli # Install shell dependencies needed by CI tests. i-bash-ci: diff --git a/skillsSync.test.ts b/skillsSync.test.ts index 305b8fa..f94ede5 100644 --- a/skillsSync.test.ts +++ b/skillsSync.test.ts @@ -19,7 +19,7 @@ const makeSkill = async ( const runSkillsSync = async ( homeDir: string, - bokuPath: string, + repoPath: string, args: string[] = ["--run"], ): Promise => { const proc = Bun.spawn(["bun", scriptPath, ...args], { @@ -27,7 +27,7 @@ const runSkillsSync = async ( env: { ...process.env, HOME: homeDir, - BOKU_PATH: bokuPath, + REPO: repoPath, }, stderr: "pipe", stdout: "pipe", @@ -57,11 +57,11 @@ describe("skills-sync", () => { let testCfg: { root: string syncHomeDir: string - syncBokuPath: string + syncRepoPath: string syncPublicDir: string syncPrivateDir: string dryRunHomeDir: string - dryRunBokuPath: string + dryRunRepoPath: string dryRunPublicDir: string } @@ -69,7 +69,8 @@ describe("skills-sync", () => { const root = await mkdtemp(join(tmpdir(), "skills-sync-test-")) const syncHomeDir = join(root, "sync-home") - const syncBokuPath = join(root, "sync-boku") + const syncRepoPath = join(root, "sync-repo") + const syncBokuPath = join(syncRepoPath, "boku") const syncRuntimeDir = join(syncHomeDir, ".cursor/skills") const syncPublicDir = join(syncBokuPath, "jiyuu/ai-skills") const syncPrivateDir = join(syncBokuPath, "priv-skills") @@ -111,7 +112,8 @@ describe("skills-sync", () => { ) const dryRunHomeDir = join(root, "dry-run-home") - const dryRunBokuPath = join(root, "dry-run-boku") + const dryRunRepoPath = join(root, "dry-run-repo") + const dryRunBokuPath = join(dryRunRepoPath, "boku") const dryRunRuntimeDir = join(dryRunHomeDir, ".cursor/skills") const dryRunPublicDir = join(dryRunBokuPath, "jiyuu/ai-skills") @@ -122,23 +124,23 @@ describe("skills-sync", () => { testCfg = { root, syncHomeDir, - syncBokuPath, + syncRepoPath, syncPublicDir, syncPrivateDir, dryRunHomeDir, - dryRunBokuPath, + dryRunRepoPath, dryRunPublicDir, } }) test("defaults to dry run unless --run is passed", async () => { - await runSkillsSync(testCfg.dryRunHomeDir, testCfg.dryRunBokuPath, []) + await runSkillsSync(testCfg.dryRunHomeDir, testCfg.dryRunRepoPath, []) expect(await dirNames(testCfg.dryRunPublicDir)).toEqual(["stale-public"]) }) test("syncs direct runtime skills to public and private repos", async () => { - await runSkillsSync(testCfg.syncHomeDir, testCfg.syncBokuPath) + await runSkillsSync(testCfg.syncHomeDir, testCfg.syncRepoPath) expect(await dirNames(testCfg.syncPublicDir)).toEqual([ "public-false", diff --git a/skillsSync.ts b/skillsSync.ts index e3f70fe..6b7374a 100644 --- a/skillsSync.ts +++ b/skillsSync.ts @@ -40,16 +40,18 @@ if (help) { const defaultConfig = (env: NodeJS.ProcessEnv): SkillsSyncConfig => { const home = env.HOME - const bokuPath = env.BOKU_PATH + const repo = env.REPO if (!home) { throw new Error("HOME is not set") } - if (!bokuPath) { - throw new Error("BOKU_PATH is not set") + if (!repo) { + throw new Error("REPO is not set") } + const bokuPath = resolve(repo, "boku") + return { skillsRuntime: resolve(home, ".cursor/skills"), publicSkillsDir: resolve(bokuPath, "jiyuu/ai-skills"), diff --git a/zsh/aliases.sh b/zsh/aliases.sh index 5f85a49..92a6053 100644 --- a/zsh/aliases.sh +++ b/zsh/aliases.sh @@ -2,28 +2,27 @@ alias ohmyzsh="nvim ~/.oh-my-zsh" -# MongoDB alias commands +# MongoDB alias commands alias mongo="/mnt/c/Program\ Files/MongoDB/Server/5.0/bin/mongo.exe" alias mongod="/mnt/c/Program\ Files/MongoDB/Server/5.0/bin/mongod.exe" alias mongosh="/mnt/c/Users/realu/AppData/Local/Programs/mongosh/mongosh.exe" alias mongos="/mnt/c/Program\ Files/MongoDB/Server/5.0/bin/mongos.exe" # Unique Divine linux commands -alias ls="exa" # cargo install exa -alias la='exa -a' -alias s='sudo' -alias yarn-offline='yarn --prefer-offline' -alias cat="bat" # cargo install bat +alias ls="exa" # cargo install exa +alias la="exa -a" +alias s="sudo" +alias yarn-offline="yarn --prefer-offline" +alias cat="bat" # cargo install bat # NOTE: This is important - sudo apt install build-essential +alias rm="trash -v" alias vi="nvim" alias vim="nvim" -alias rt="ud rs test-short" - # vv: short for "vim view". Piping to vv lets you read stdout in a vi editor alias vv="view -" -alias vvjq="jq | vv" +alias vvjq="jq | view -" alias ess="2>&1" @@ -34,7 +33,7 @@ git config --global alias.co checkout git config --global alias.br branch git config --global alias.ci commit git config --global alias.st status -git config --global alias.ac '!git add -A && git commit' +git config --global alias.ac "!git add -A && git commit" git config --global core.editor "nvim" # Broadcast tx and open it in vim. diff --git a/zsh/quick.sh b/zsh/quick.sh index 375bb1a..deb9d74 100644 --- a/zsh/quick.sh +++ b/zsh/quick.sh @@ -4,7 +4,7 @@ # ℹ️ -------- IMPORTS -------- ℹ️ source "$DOTFILES/zsh/bashlib.sh" -# The $DOTFILES and $BOKU_PATH variables are exported from .zshenv. +# The $REPO and $DOTFILES variables are exported from .zshenv. # ----------------- Git ----------------- # Some of my most-used commands. I rely on these daily to quickly finish @@ -73,19 +73,29 @@ git_cipb() { # your text-based TODO-list open. todos() { local before="$(pwd)" - cd "$BOKU_PATH" || return 1 - nvim "$BOKU_PATH/free/todos.md" + cd "$REPO/boku" || return 1 + nvim "$REPO/boku/free/todos.md" cd "$before" || return 1 } # notes: Opens NeoVim with a notes workspace as the working directory. notes() { local before="$(pwd)" - cd "$BOKU_PATH" || return 1 - nvim "$BOKU_PATH/free/the-log.md" + cd "$REPO/boku" || return 1 + nvim "$REPO/boku/free/the-log.md" cd "$before" || return 1 } +# todos: Opens NeoVim with your notes workspace as the working directory with +# your text-based TODO-list open. +epics() { + ( + # subshell retains path from prior to the edit + cd "$REPO/boku" + nvim "$REPO/boku/epics" + ) +} + # music: Opens the Windows file explorer to your music files. music() { local before="$(pwd)" @@ -129,7 +139,7 @@ skills() { cd "$skills_runtime" nvim "$skills_runtime" - cd "$BOKU_PATH/dotfiles" + cd "$REPO/boku/dotfiles" just skills-sync --run cd "$before" @@ -156,7 +166,7 @@ cfg_tmux() { # sharex: ShareX screenshots CLI. Pass --run (nvim) or --explorer; no args -> help. sharex() { - bash "$BOKU_PATH/sharex.sh" "$@" + bash "$REPO/boku/sharex.sh" "$@" } # ----------------- Nibiru ----------------- diff --git a/zsh/zshenv b/zsh/zshenv index 199a40b..bd25b65 100644 --- a/zsh/zshenv +++ b/zsh/zshenv @@ -1,7 +1,7 @@ . "$HOME/.cargo/env" -export BOKU_PATH="$HOME/ki/boku" -export DOTFILES="$BOKU_PATH/dotfiles" +export REPO="$HOME/ki" +export DOTFILES="$REPO/boku/dotfiles" source "$DOTFILES/symlinks.sh" export OUTTXT="$HOME/ki/out.txt" export SKILLS="$HOME/.cursor/skills" -export EPICS="$BOKU_PATH/epics" +export EPICS="$REPO/boku/epics" diff --git a/zsh/zshrc b/zsh/zshrc index c5595b0..0eb7662 100644 --- a/zsh/zshrc +++ b/zsh/zshrc @@ -3,7 +3,7 @@ # Contact: Unique Divine # The .zshrc runs after .zshenv -# The $DOTFILES and $BOKU_PATH variables are exported from .zshenv. +# The $REPO and $DOTFILES variables are exported from .zshenv. source $DOTFILES/zsh/bashlib.sh main_bash_setup @@ -351,3 +351,7 @@ sync_cursor_cli_config source ~/powerlevel10k/powerlevel10k.zsh-theme [[ -s "$HOME/.gvm/scripts/gvm" ]] && source "$HOME/.gvm/scripts/gvm" + +# >>> Codex installer >>> +export PATH="$HOME/.local/bin:$PATH" +# <<< Codex installer <<<