mirror of
https://github.com/anotherhadi/blog.git
synced 2026-05-20 13:32:33 +02:00
d6d410a2fa
Signed-off-by: Hadi <hadi@example.com>
121 lines
3.5 KiB
Svelte
121 lines
3.5 KiB
Svelte
<script lang="ts">
|
|
import { onMount, untrack } from "svelte";
|
|
|
|
interface Props {
|
|
vars: string[];
|
|
}
|
|
|
|
const { vars }: Props = $props();
|
|
|
|
let values = $state<Record<string, string>>(
|
|
untrack(() => Object.fromEntries(vars.map((v) => [v, ""]))),
|
|
);
|
|
let open = $state(false);
|
|
let applied = $state(false);
|
|
|
|
const originals = new Map<Text, string>();
|
|
|
|
onMount(() => {
|
|
const content = document.querySelector(".note-content");
|
|
if (!content) return;
|
|
const walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);
|
|
let node: Text | null;
|
|
while ((node = walker.nextNode() as Text | null)) {
|
|
if (/\$[a-zA-Z_][a-zA-Z0-9_]*/.test(node.nodeValue ?? "")) {
|
|
originals.set(node, node.nodeValue!);
|
|
}
|
|
}
|
|
});
|
|
|
|
function applyVars() {
|
|
const sorted = [...vars].sort((a, b) => b.length - a.length);
|
|
for (const [node, original] of originals) {
|
|
let text = original;
|
|
for (const name of sorted) {
|
|
const val = values[name];
|
|
if (val) {
|
|
text = text.replace(
|
|
new RegExp(`\\$${name}(?![a-zA-Z0-9_])`, "g"),
|
|
val,
|
|
);
|
|
}
|
|
}
|
|
node.nodeValue = text;
|
|
}
|
|
applied = true;
|
|
setTimeout(() => (applied = false), 1800);
|
|
}
|
|
</script>
|
|
|
|
{#if vars.length > 0}
|
|
<button
|
|
onclick={() => (open = true)}
|
|
class="btn btn-ghost btn-xs font-mono text-base-content/40 hover:text-base-content/70 border border-base-300/50"
|
|
>
|
|
<span class="font-mono text-[10px] leading-none">$</span>
|
|
vars
|
|
</button>
|
|
|
|
{#if open}
|
|
<dialog class="modal modal-open">
|
|
<div
|
|
class="modal-box max-w-sm"
|
|
style="background: oklch(10% 0 0); border: 1px solid oklch(71% 0.0863 296.59 / 0.5);"
|
|
>
|
|
<h3
|
|
class="font-mono text-[10px] text-base-content/40 uppercase tracking-widest mb-4"
|
|
>
|
|
variables
|
|
</h3>
|
|
<div class="space-y-2.5">
|
|
{#each vars as v}
|
|
<div class="flex items-center gap-3">
|
|
<label
|
|
for={`var-${v}`}
|
|
class="font-mono text-xs text-primary/70 w-36 shrink-0 truncate"
|
|
title={`$${v}`}
|
|
>
|
|
${v}
|
|
</label>
|
|
<input
|
|
id={`var-${v}`}
|
|
type="text"
|
|
bind:value={values[v]}
|
|
placeholder={`$${v}`}
|
|
class="input input-sm flex-1 min-w-0 font-mono text-xs bg-base-300/20 border-base-300/60 text-base-content/80 placeholder:text-base-content/25 focus:border-primary/60"
|
|
/>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
<div class="mt-5 flex items-center justify-between">
|
|
<span
|
|
class="font-mono text-[10px] text-primary/60 transition-opacity duration-300"
|
|
class:opacity-0={!applied}
|
|
>
|
|
✓ applied
|
|
</span>
|
|
<div class="flex gap-2">
|
|
<button
|
|
onclick={applyVars}
|
|
class="btn btn-ghost btn-xs font-mono text-primary/60 hover:text-primary border border-primary/30"
|
|
>
|
|
apply
|
|
</button>
|
|
<button
|
|
onclick={() => (open = false)}
|
|
class="btn btn-ghost btn-xs font-mono text-base-content/40 hover:text-base-content/70 border border-base-300/50"
|
|
>
|
|
close
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button
|
|
onclick={() => (open = false)}
|
|
class="modal-backdrop"
|
|
aria-label="close"
|
|
></button>
|
|
</dialog>
|
|
{/if}
|
|
{/if}
|