Kokkoja: first public assembly
Assembled from the development repository by tools/make_release.sh. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
commit
95c80d04c9
39 files changed
+9905
No files matched your search
@@ -0,0 +1,67 @@
|
||||
// @ts-check
|
||||
/**
|
||||
* Files outside the notebooks: image files to put on a page, and the place
|
||||
* a picture of a page is exported to. A web page cannot look through the
|
||||
* user's directories or write where the user chooses, so it asks the local
|
||||
* program (kokkoja.py), as it does for the notebooks.
|
||||
*/
|
||||
|
||||
import { askLocalProgram } from './notebook.js';
|
||||
|
||||
/**
|
||||
* @typedef {object} DirectoryListing
|
||||
* @property {string} directory Its full path.
|
||||
* @property {string | null} parent Null at the top of the file system.
|
||||
* @property {{name: string, path: string}[]} directories
|
||||
* @property {{name: string, path: string, size: number}[]} files Image files only.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Response} response
|
||||
* @returns {Promise<string>} What the local program says went wrong.
|
||||
*/
|
||||
async function reason(response) {
|
||||
try {
|
||||
return String((await response.json()).error);
|
||||
} catch {
|
||||
return `error ${response.status}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [directory] A full path; without one, where the notebooks are.
|
||||
* @returns {Promise<DirectoryListing>}
|
||||
*/
|
||||
export async function listFiles(directory = '') {
|
||||
const response = await askLocalProgram(`api/files?directory=${encodeURIComponent(directory)}`);
|
||||
if (!response.ok) throw new Error(`${directory}: ${await reason(response)}`);
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path The full path of an image file, from a listing.
|
||||
* @returns {Promise<Blob>}
|
||||
*/
|
||||
export async function readFile(path) {
|
||||
const response = await askLocalProgram(`api/file?path=${encodeURIComponent(path)}`);
|
||||
if (!response.ok) throw new Error(`${path}: ${await reason(response)}`);
|
||||
return response.blob();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a PNG picture into a directory.
|
||||
* @param {string} directory Its full path.
|
||||
* @param {string} name Ending in .png.
|
||||
* @param {Blob} picture
|
||||
* @param {boolean} replace Whether a file of that name may be replaced.
|
||||
* @returns {Promise<string | null>} The full path written, or null if a
|
||||
* file of that name exists and may not be replaced.
|
||||
*/
|
||||
export async function writePicture(directory, name, picture, replace) {
|
||||
const query = `directory=${encodeURIComponent(directory)}&name=${encodeURIComponent(name)}` +
|
||||
(replace ? '&replace=1' : '');
|
||||
const response = await askLocalProgram(`api/file?${query}`, 'PUT', picture);
|
||||
if (response.status === 409) return null;
|
||||
if (!response.ok) throw new Error(await reason(response));
|
||||
return (await response.json()).saved;
|
||||
}
|
||||
Reference in new issue
Block a user