FileEdit.tsx
Raw
1import type { RepositoryRow } from "../../db/index.ts";
2import type { SessionUser } from "../../middleware/session.ts";
3import { MAX_FILE_PATH_LENGTH } from "../../constants.ts";
4import { Layout } from "../layout.tsx";
5import { RepoHeader } from "../repos/RepoHeader.tsx";
6import { RepoNav } from "./RepoNav.tsx";
7
8interface FileEditProps {
9 user: SessionUser;
10 repo: RepositoryRow;
11 ref: string;
12 filePath: string;
13 content: string;
14 error?: string;
15 queryError?: string;
16}
17
18export function FileEdit({
19 user,
20 repo,
21 ref: editRef,
22 filePath,
23 content,
24 error,
25 queryError,
26}: FileEditProps) {
27 const parts = filePath.split("/");
28 const filename = parts[parts.length - 1] ?? filePath;
29 return (
30 <Layout user={user} title={`Edit ${repo.name}/${filePath}`}>
31 <div class="container">
32 <RepoHeader repo={repo} />
33 <RepoNav repo={repo} active="code" user={user} />
34 <div class="breadcrumb">
35 <a href={`/${repo.name}/tree/${editRef}`}>{repo.name}</a>
36 {parts.map((part, i) => {
37 const partPath = parts.slice(0, i + 1).join("/");
38 const isLast = i === parts.length - 1;
39 return (
40 <>
41 <span class="breadcrumb-sep">/</span>
42 {isLast ? (
43 <span class="breadcrumb-current">
44 {part}
45 </span>
46 ) : (
47 <a
48 href={`/${repo.name}/tree/${editRef}/${partPath}`}
49 >
50 {part}
51 </a>
52 )}
53 </>
54 );
55 })}
56 </div>
57 <p class="form-hint">
58 WARNING: Line endings are normalized to LF (\n) on save.
59 </p>
60 {(error || queryError) && (
61 <p class="form-error">{error ?? queryError}</p>
62 )}
63 <form
64 method="POST"
65 action={`/${repo.name}/edit/${editRef}/${filePath}`}
66 >
67 <div class="file-blob-header">
68 <span class="file-blob-name">{filename}</span>
69 <div class="file-blob-actions">
70 <a
71 href={`/${repo.name}/blob/${editRef}/${filePath}`}
72 class="btn btn-sm"
73 >
74 Cancel
75 </a>
76 </div>
77 </div>
78 <div class="file-blob-body">
79 <textarea
80 name="content"
81 class="file-edit-textarea"
82 rows="30"
83 spellcheck="false"
84 autocorrect="off"
85 autocapitalize="off"
86 {...{ autocomplete: "off" }}
87 >
88 {content}
89 </textarea>
90 </div>
91 <div class="form-card">
92 <p
93 class="form-hint"
94 style="margin-bottom: var(--space-4);"
95 >
96 Committing directly to <strong>{editRef}</strong>
97 </p>
98 <div class="form-group">
99 <label for="file-path">File path</label>
100 <input
101 id="file-path"
102 name="new_path"
103 type="text"
104 value={filePath}
105 class="mono"
106 maxlength={MAX_FILE_PATH_LENGTH}
107 />
108 </div>
109 <div class="form-group">
110 <label for="message">Commit message</label>
111 <textarea
112 id="message"
113 name="message"
114 rows="3"
115 required
116 >
117 {`Edited ${filename}`}
118 </textarea>
119 </div>
120 <div class="form-actions">
121 <button type="submit" class="btn btn-primary">
122 Commit changes
123 </button>
124 <a
125 href={`/${repo.name}/blob/${editRef}/${filePath}`}
126 class="btn"
127 >
128 Cancel
129 </a>
130 </div>
131 </div>
132 </form>
133 </div>
134 </Layout>
135 );
136}
137