@firstpick/pi-package-webui 0.2.9 → 0.3.0
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/bin/pi-webui.mjs +38 -1
- package/package.json +1 -1
- package/public/app.js +778 -59
- package/public/index.html +51 -10
- package/public/styles.css +457 -17
- package/tests/mobile-static.test.mjs +60 -4
package/bin/pi-webui.mjs
CHANGED
|
@@ -1419,6 +1419,33 @@ async function getDirectoryPickerData(viewPath, activeCwd) {
|
|
|
1419
1419
|
};
|
|
1420
1420
|
}
|
|
1421
1421
|
|
|
1422
|
+
function cleanDirectoryCreateName(value) {
|
|
1423
|
+
const name = String(value || "").trim();
|
|
1424
|
+
if (!name) throw makeHttpError(400, "Directory name is required");
|
|
1425
|
+
if (name === "." || name === "..") throw makeHttpError(400, "Directory name cannot be . or ..");
|
|
1426
|
+
if (name.includes("\u0000")) throw makeHttpError(400, "Directory name cannot contain null bytes");
|
|
1427
|
+
if (name.includes("/") || name.includes("\\")) throw makeHttpError(400, "Create one directory at a time; path separators are not allowed");
|
|
1428
|
+
if (name.length > 255) throw makeHttpError(400, "Directory name is too long");
|
|
1429
|
+
return name;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
async function createDirectoryPickerDirectory(parentPath, nameValue, activeCwd) {
|
|
1433
|
+
const parent = await resolveCwd(parentPath || activeCwd, activeCwd);
|
|
1434
|
+
const name = cleanDirectoryCreateName(nameValue);
|
|
1435
|
+
const target = path.resolve(parent, name);
|
|
1436
|
+
if (path.dirname(target) !== parent) throw makeHttpError(400, "Directory must be created directly under the current path");
|
|
1437
|
+
|
|
1438
|
+
try {
|
|
1439
|
+
await mkdir(target);
|
|
1440
|
+
} catch (error) {
|
|
1441
|
+
if (error?.code === "EEXIST") throw makeHttpError(409, `Directory already exists: ${target}`);
|
|
1442
|
+
if (["EACCES", "EPERM"].includes(error?.code)) throw makeHttpError(403, `Cannot create directory ${target}: ${sanitizeError(error)}`);
|
|
1443
|
+
throw makeHttpError(400, `Cannot create directory ${target}: ${sanitizeError(error)}`);
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
return getDirectoryPickerData(target, activeCwd);
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1422
1449
|
function normalizeSuggestionPath(value) {
|
|
1423
1450
|
return String(value || "").replace(/\\/g, "/");
|
|
1424
1451
|
}
|
|
@@ -2688,7 +2715,7 @@ async function restorableTabsForRestart() {
|
|
|
2688
2715
|
const state = await currentSessionState(tab).catch(() => tab.lastState || null);
|
|
2689
2716
|
return restorableTabDescriptor(tab, state);
|
|
2690
2717
|
}));
|
|
2691
|
-
return mergeRestorableTabDescriptors(liveDescriptors
|
|
2718
|
+
return mergeRestorableTabDescriptors(liveDescriptors);
|
|
2692
2719
|
}
|
|
2693
2720
|
|
|
2694
2721
|
function spawnRestartServer(restorableTabs) {
|
|
@@ -4288,6 +4315,16 @@ const server = createServer(async (req, res) => {
|
|
|
4288
4315
|
return;
|
|
4289
4316
|
}
|
|
4290
4317
|
|
|
4318
|
+
if (url.pathname === "/api/directories" && req.method === "POST") {
|
|
4319
|
+
const body = await readJsonBody(req);
|
|
4320
|
+
const tab = getRequestedTab(req, url, body);
|
|
4321
|
+
sendJson(res, 201, {
|
|
4322
|
+
ok: true,
|
|
4323
|
+
data: await createDirectoryPickerDirectory(body.parent ?? body.cwd ?? body.path, body.name, tab.cwd),
|
|
4324
|
+
});
|
|
4325
|
+
return;
|
|
4326
|
+
}
|
|
4327
|
+
|
|
4291
4328
|
if (url.pathname === "/api/path-suggestions" && req.method === "GET") {
|
|
4292
4329
|
const tab = getRequestedTab(req, url);
|
|
4293
4330
|
sendJson(res, 200, { ok: true, data: await getPathSuggestionData(tab, url.searchParams.get("query")) });
|
package/package.json
CHANGED