seemore 1.12.20 → 1.12.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/dist/cli/index.js +43 -8
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/src/app/features/edit.tsx +77 -16
- package/src/app/styles/globals.css +4 -2
package/README.md
CHANGED
|
@@ -64,7 +64,7 @@ npx seemore # renders this folder at http://localhost:4040
|
|
|
64
64
|
|
|
65
65
|
The [seemore website](https://arifszn.github.io/seemore) is a folder of Markdown, rendered by seemore. Your own folder will look the same way.
|
|
66
66
|
|
|
67
|
-
<details
|
|
67
|
+
<details>
|
|
68
68
|
<summary><strong>Or ask an AI agent to set it up</strong></summary>
|
|
69
69
|
<br/>
|
|
70
70
|
|
|
@@ -114,7 +114,7 @@ Point seemore at any Markdown you already have: AI-written notes, project docs,
|
|
|
114
114
|
- **Zero config**: no config file, no code, no files to move
|
|
115
115
|
- **Live preview**: files appear, disappear and reorder as you edit them
|
|
116
116
|
- **Edit in place**: double-click any block in the preview to fix its Markdown
|
|
117
|
-
- **Desktop app**: for macOS, Windows and Linux
|
|
117
|
+
- **Desktop app**: for macOS, Windows and Linux, with an integrated terminal
|
|
118
118
|
- **Editor integration**: one extension covers VS Code, Cursor, Antigravity and other VS Code-compatible editors, including remote workspaces
|
|
119
119
|
- **Documentation framework**: `seemore build` renders the whole site to HTML, ready to deploy on any host
|
|
120
120
|
- **Password protection**: protect the built site with one shared password, with no server required
|
|
@@ -136,11 +136,15 @@ Inline editing works only for local previews.
|
|
|
136
136
|
|
|
137
137
|
## Open with the desktop app
|
|
138
138
|
|
|
139
|
-
Download the app from the [download page](https://arifszn.github.io/seemore/download). It opens a folder or a `.md` file as a site in its own window
|
|
139
|
+
Download the app from the [download page](https://arifszn.github.io/seemore/download). It opens a folder or a `.md` file as a site in its own window, with an integrated terminal.
|
|
140
|
+
|
|
141
|
+
<p align="center">
|
|
142
|
+
<img src="https://raw.githubusercontent.com/arifszn/seemore/main/packages/site/assets/desktop-app.png" alt="The seemore desktop app in the black theme, showing a folder of research notes as a site: a paragraph's Markdown source open in the inline editor above a flowchart, and the integrated terminal listing the folder's Markdown files" width="640"/>
|
|
143
|
+
</p>
|
|
140
144
|
|
|
141
145
|
- **Open a folder**: use **File > Open Folder**, or drag the folder onto the app.
|
|
142
146
|
- **Open a file**: double-click a `.md` file, or use **File > Open File**.
|
|
143
|
-
- **
|
|
147
|
+
- **Use the integrated terminal**: it opens in the site's folder, so you can run an AI agent, git or a script next to the pages it changes.
|
|
144
148
|
|
|
145
149
|
## View in your code editor
|
|
146
150
|
|
package/dist/cli/index.js
CHANGED
|
@@ -1159,11 +1159,11 @@ function humanise(slug) {
|
|
|
1159
1159
|
// src/node/content/source.ts
|
|
1160
1160
|
function createSource(options) {
|
|
1161
1161
|
let cached;
|
|
1162
|
-
const
|
|
1162
|
+
const read2 = () => cached ??= scan(options);
|
|
1163
1163
|
const loader = dynamicLoader(
|
|
1164
1164
|
{
|
|
1165
1165
|
cache: "custom",
|
|
1166
|
-
files: () =>
|
|
1166
|
+
files: () => read2().files,
|
|
1167
1167
|
invalidate: () => {
|
|
1168
1168
|
cached = void 0;
|
|
1169
1169
|
}
|
|
@@ -1172,11 +1172,11 @@ function createSource(options) {
|
|
|
1172
1172
|
);
|
|
1173
1173
|
return {
|
|
1174
1174
|
loader,
|
|
1175
|
-
current:
|
|
1176
|
-
pages: () =>
|
|
1175
|
+
current: read2,
|
|
1176
|
+
pages: () => read2().pages,
|
|
1177
1177
|
refresh() {
|
|
1178
1178
|
loader.invalidate();
|
|
1179
|
-
return
|
|
1179
|
+
return read2();
|
|
1180
1180
|
},
|
|
1181
1181
|
async getPageTree() {
|
|
1182
1182
|
const output = await loader.get();
|
|
@@ -1822,6 +1822,14 @@ function spliceSource(content, request) {
|
|
|
1822
1822
|
}
|
|
1823
1823
|
return { ok: true, content: content.slice(0, start) + withEol(text, dominantEol(content)) + content.slice(end) };
|
|
1824
1824
|
}
|
|
1825
|
+
function appendSource(content, text) {
|
|
1826
|
+
const body = text.replace(/\r\n/g, "\n").replace(/\s+$/, "");
|
|
1827
|
+
if (body.trim() === "") return void 0;
|
|
1828
|
+
const lf = content.replace(/\r\n/g, "\n");
|
|
1829
|
+
const separator = lf === "" || lf.endsWith("\n\n") ? "" : lf.endsWith("\n") ? "\n" : "\n\n";
|
|
1830
|
+
return content + withEol(`${separator}${body}
|
|
1831
|
+
`, dominantEol(content));
|
|
1832
|
+
}
|
|
1825
1833
|
function dominantEol(content) {
|
|
1826
1834
|
const crlf = content.match(/\r\n/g)?.length ?? 0;
|
|
1827
1835
|
const lf = (content.match(/\n/g)?.length ?? 0) - crlf;
|
|
@@ -2018,7 +2026,8 @@ function handleSourceRead(ctx, req, res) {
|
|
|
2018
2026
|
if (page === void 0) return send(res, 404, { error: "That file is not part of this site." });
|
|
2019
2027
|
const start = Number(query.get("start"));
|
|
2020
2028
|
const end = Number(query.get("end"));
|
|
2021
|
-
const content =
|
|
2029
|
+
const content = read(res, page.absPath);
|
|
2030
|
+
if (content === void 0) return;
|
|
2022
2031
|
if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < start || end > content.length) {
|
|
2023
2032
|
return send(res, 400, { error: "The requested range is not inside this file." });
|
|
2024
2033
|
}
|
|
@@ -2033,10 +2042,19 @@ async function handleSourceWrite(ctx, req, res) {
|
|
|
2033
2042
|
}
|
|
2034
2043
|
const page = resolvePage(ctx, body.file);
|
|
2035
2044
|
if (page === void 0) return send(res, 404, { error: "That file is not part of this site." });
|
|
2045
|
+
if (body.append === true) {
|
|
2046
|
+
if (typeof body.text !== "string") return send(res, 400, { error: "`text` is required." });
|
|
2047
|
+
const content2 = read(res, page.absPath);
|
|
2048
|
+
if (content2 === void 0) return;
|
|
2049
|
+
const appended = appendSource(content2, body.text);
|
|
2050
|
+
if (appended === void 0) return send(res, 200, { ok: true });
|
|
2051
|
+
return write(res, page.absPath, appended);
|
|
2052
|
+
}
|
|
2036
2053
|
if (typeof body.expected !== "string" || typeof body.text !== "string") {
|
|
2037
2054
|
return send(res, 400, { error: "Both `expected` and `text` are required." });
|
|
2038
2055
|
}
|
|
2039
|
-
const content =
|
|
2056
|
+
const content = read(res, page.absPath);
|
|
2057
|
+
if (content === void 0) return;
|
|
2040
2058
|
const result = spliceSource(content, {
|
|
2041
2059
|
start: body.start,
|
|
2042
2060
|
end: body.end,
|
|
@@ -2044,7 +2062,24 @@ async function handleSourceWrite(ctx, req, res) {
|
|
|
2044
2062
|
text: body.text
|
|
2045
2063
|
});
|
|
2046
2064
|
if (!result.ok) return send(res, result.status, { error: result.error });
|
|
2047
|
-
|
|
2065
|
+
return write(res, page.absPath, result.content);
|
|
2066
|
+
}
|
|
2067
|
+
function read(res, file) {
|
|
2068
|
+
try {
|
|
2069
|
+
return readFileSync6(file, "utf8");
|
|
2070
|
+
} catch (error) {
|
|
2071
|
+
const code = error.code;
|
|
2072
|
+
send(res, 500, { error: `The file could not be read${code === void 0 ? "" : ` (${code})`}. Is another program holding it open?` });
|
|
2073
|
+
return void 0;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
function write(res, file, content) {
|
|
2077
|
+
try {
|
|
2078
|
+
writeFileSync5(file, content, "utf8");
|
|
2079
|
+
} catch (error) {
|
|
2080
|
+
const code = error.code;
|
|
2081
|
+
return send(res, 500, { error: `The file could not be written${code === void 0 ? "" : ` (${code})`}. Is another program holding it open?` });
|
|
2082
|
+
}
|
|
2048
2083
|
return send(res, 200, { ok: true });
|
|
2049
2084
|
}
|
|
2050
2085
|
function resolvePage(ctx, file) {
|