This repository has no description
1<script lang="ts">
2 import { untrack } from "svelte";
3 import { now as tidNow } from "@atcute/tid";
4 import Button from "$lib/components/ui/Button.svelte";
5 import Spinner from "$lib/components/ui/Spinner.svelte";
6 import ErrorAlert from "$lib/components/ui/Error.svelte";
7 import ArrowUp from "$icon/arrow-up";
8 import X from "$icon/x";
9 import { getAuth } from "$lib/auth.svelte";
10 import { putString } from "$lib/api/strings";
11 import type { StringRecord } from "$lib/api/records";
12 import { createAction } from "$lib/action.svelte";
13
14 interface Props {
15 mode?: "create" | "edit";
16 rkey?: string;
17 createdAt?: string;
18 filename?: string;
19 description?: string;
20 content?: string;
21 onsaved: (rkey: string) => void;
22 oncancel?: () => void;
23 }
24
25 let {
26 mode = "create",
27 rkey,
28 createdAt,
29 filename: initialFilename = "",
30 description: initialDescription = "",
31 content: initialContent = "",
32 onsaved,
33 oncancel
34 }: Props = $props();
35
36 const auth = getAuth();
37
38 let filename = $state(untrack(() => initialFilename));
39 let description = $state(untrack(() => initialDescription));
40 let content = $state(untrack(() => initialContent));
41
42 const lineCount = $derived(content === "" ? 0 : content.split("\n").length);
43 const byteCount = $derived(new TextEncoder().encode(content).length);
44
45 const publish = createAction(async (event: SubmitEvent) => {
46 event.preventDefault();
47 const agent = auth.agent;
48 if (!agent) return;
49 const targetRkey = rkey ?? tidNow();
50 const record: StringRecord = {
51 $type: "sh.tangled.string",
52 filename,
53 description,
54 contents: content,
55 createdAt: createdAt ?? new Date().toISOString()
56 };
57 await putString(agent, targetRkey, record);
58 onsaved(targetRkey);
59 });
60</script>
61
62<div class="rounded-sm border border-border-default bg-background-default">
63 <form onsubmit={publish.run}>
64 <div class="flex flex-col gap-2 p-4">
65 <div class="flex flex-col gap-2 md:flex-row">
66 <input
67 type="text"
68 id="filename"
69 name="filename"
70 placeholder="Filename"
71 required
72 bind:value={filename}
73 disabled={publish.loading}
74 class="rounded-sm border border-border-default bg-background-default p-2 text-sm text-foreground-default placeholder:text-foreground-placeholder focus:ring-1 focus:ring-border-strong focus:outline-none disabled:opacity-50 md:max-w-64"
75 />
76 <input
77 type="text"
78 id="description"
79 name="description"
80 placeholder="Description ..."
81 maxlength={280}
82 bind:value={description}
83 disabled={publish.loading}
84 class="flex-1 rounded-sm border border-border-default bg-background-default p-2 text-sm text-foreground-default placeholder:text-foreground-placeholder focus:ring-1 focus:ring-border-strong focus:outline-none disabled:opacity-50"
85 />
86 </div>
87
88 <textarea
89 id="content"
90 name="content"
91 placeholder="Paste your string here!"
92 required
93 rows={20}
94 spellcheck={false}
95 bind:value={content}
96 disabled={publish.loading}
97 class="w-full resize-y rounded-sm border border-border-default bg-background-default p-2 font-mono text-sm text-foreground-default placeholder:text-foreground-placeholder focus:ring-1 focus:ring-border-strong focus:outline-none disabled:opacity-50"
98 ></textarea>
99 </div>
100
101 <div class="flex items-center justify-between px-4 py-3">
102 <div class="text-sm text-foreground-subtle">
103 <span>{lineCount} line{lineCount !== 1 ? "s" : ""}</span>
104 <span class="px-1 select-none">·</span>
105 <span>{byteCount} byte{byteCount !== 1 ? "s" : ""}</span>
106 </div>
107
108 <div class="flex items-center gap-2">
109 {#if mode === "edit"}
110 <Button
111 type="button"
112 variant="default"
113 icon={X}
114 disabled={publish.loading}
115 onclick={oncancel}
116 >
117 Cancel
118 </Button>
119 {/if}
120 <Button
121 type="submit"
122 variant="primary"
123 icon={publish.loading ? Spinner : ArrowUp}
124 disabled={publish.loading}
125 >
126 Publish
127 </Button>
128 </div>
129 </div>
130
131 {#if publish.error}
132 <div class="px-4 pb-4">
133 <ErrorAlert label={publish.error} />
134 </div>
135 {/if}
136 </form>
137</div>