@inkandswitch/patchwork-pkg-base 0.0.77 → 0.0.79
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/module-settings-url +1 -1
- package/package.json +1 -1
- package/packages/folder/dist/chunk-WN53YCSZ.js +62 -0
- package/packages/folder/dist/chunk-WN53YCSZ.js.map +7 -0
- package/packages/folder/dist/datatype-AGCTQZ52.js +41 -0
- package/packages/folder/dist/datatype-AGCTQZ52.js.map +7 -0
- package/packages/folder/dist/index.js +14 -3
- package/packages/folder/dist/index.js.map +2 -2
- package/packages/folder/dist/{tool-EOCAL24F.js → tool-JSQNX2I6.js} +85 -20
- package/packages/folder/dist/tool-JSQNX2I6.js.map +7 -0
- package/packages/packages/dist/index.js +2 -2
- package/plugin-registry-url +1 -1
- package/packages/folder/dist/datatype-HWGUFBTQ.js +0 -27
- package/packages/folder/dist/datatype-HWGUFBTQ.js.map +0 -7
- package/packages/folder/dist/tool-EOCAL24F.js.map +0 -7
package/module-settings-url
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
automerge:
|
|
1
|
+
automerge:33GLVfPfbJyp9X1keJrvthsJUaKf
|
package/package.json
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// src/links.js
|
|
2
|
+
import {
|
|
3
|
+
isImmutableString,
|
|
4
|
+
isValidAutomergeUrl
|
|
5
|
+
} from "@automerge/automerge-repo/slim";
|
|
6
|
+
function asString(v) {
|
|
7
|
+
if (typeof v === "string") return v;
|
|
8
|
+
if (isImmutableString(v)) return String(v);
|
|
9
|
+
return void 0;
|
|
10
|
+
}
|
|
11
|
+
function asUrl(v) {
|
|
12
|
+
const s = asString(v);
|
|
13
|
+
return s !== void 0 && isValidAutomergeUrl(s) ? s : void 0;
|
|
14
|
+
}
|
|
15
|
+
function isDirectoryDoc(doc) {
|
|
16
|
+
return asString(doc?.["@patchwork"]?.type) === "directory";
|
|
17
|
+
}
|
|
18
|
+
var DIRECTORY_RESERVED = /* @__PURE__ */ new Set(["@patchwork", "lastSyncAt"]);
|
|
19
|
+
function fileType(name) {
|
|
20
|
+
const dot = name.lastIndexOf(".");
|
|
21
|
+
return dot > 0 ? name.slice(dot + 1) : "file";
|
|
22
|
+
}
|
|
23
|
+
function folderLinks(doc) {
|
|
24
|
+
const links = [];
|
|
25
|
+
for (const link of Array.isArray(doc?.docs) ? doc.docs : []) {
|
|
26
|
+
const url = asUrl(link?.url);
|
|
27
|
+
if (!url) continue;
|
|
28
|
+
links.push({
|
|
29
|
+
url,
|
|
30
|
+
name: asString(link.name) ?? "Untitled",
|
|
31
|
+
type: asString(link.type) ?? ""
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return links;
|
|
35
|
+
}
|
|
36
|
+
function directoryLinks(doc, path = "") {
|
|
37
|
+
const prefix = path ? `${path}/` : "";
|
|
38
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
39
|
+
const files = [];
|
|
40
|
+
for (const [key, value] of Object.entries(doc ?? {})) {
|
|
41
|
+
if (DIRECTORY_RESERVED.has(key) || !key.startsWith(prefix)) continue;
|
|
42
|
+
const url = asUrl(value);
|
|
43
|
+
if (!url) continue;
|
|
44
|
+
const rest = key.slice(prefix.length).split("/").filter(Boolean);
|
|
45
|
+
if (rest.length === 0) continue;
|
|
46
|
+
if (rest.length > 1) dirs.add(rest[0]);
|
|
47
|
+
else files.push({ url, name: rest[0], type: fileType(rest[0]) });
|
|
48
|
+
}
|
|
49
|
+
const byName = (a, b) => a.name.localeCompare(b.name);
|
|
50
|
+
return [
|
|
51
|
+
...[...dirs].map((name) => ({ name, type: "folder", path: prefix + name })).sort(byName),
|
|
52
|
+
...files.sort(byName)
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export {
|
|
57
|
+
asString,
|
|
58
|
+
isDirectoryDoc,
|
|
59
|
+
folderLinks,
|
|
60
|
+
directoryLinks
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=chunk-WN53YCSZ.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/links.js"],
|
|
4
|
+
"sourcesContent": ["import {\n isImmutableString,\n isValidAutomergeUrl,\n} from \"@automerge/automerge-repo/slim\";\n\n/**\n * A folder entry as the view sees it, whichever doc shape it came from.\n * `path` is set (and `url` isn't) for a subdirectory of a pushwork directory\n * doc, which has no doc of its own.\n * @typedef {{ name: string, type: string, url?: string, path?: string }} Link\n */\n\n// Any string in a doc may be stored as a plain string or as an\n// ImmutableString (pushwork writes links that way); read them the same.\nexport function asString(v) {\n if (typeof v === \"string\") return v;\n if (isImmutableString(v)) return String(v);\n return undefined;\n}\n\nexport function asUrl(v) {\n const s = asString(v);\n return s !== undefined && isValidAutomergeUrl(s) ? s : undefined;\n}\n\n// A pushwork \"vfs\" directory doc:\n// { \"@patchwork\": { type: \"directory\", title? }, \"a/b.txt\": url, ... }\n// Every other key is a slash-separated path to a leaf doc.\nexport function isDirectoryDoc(doc) {\n return asString(doc?.[\"@patchwork\"]?.type) === \"directory\";\n}\n\nconst DIRECTORY_RESERVED = new Set([\"@patchwork\", \"lastSyncAt\"]);\n\n// Mirrors pushwork's folder shape, which types a file link by its extension.\nfunction fileType(name) {\n const dot = name.lastIndexOf(\".\");\n return dot > 0 ? name.slice(dot + 1) : \"file\";\n}\n\n/** @returns {Link[]} the links in a folder doc's `docs` list. */\nexport function folderLinks(doc) {\n const links = [];\n for (const link of Array.isArray(doc?.docs) ? doc.docs : []) {\n const url = asUrl(link?.url);\n if (!url) continue;\n links.push({\n url,\n name: asString(link.name) ?? \"Untitled\",\n type: asString(link.type) ?? \"\",\n });\n }\n return links;\n}\n\n/**\n * The immediate children of `path` (\"\" for the top) in a directory doc:\n * subdirectories first, then files, each by name.\n * @returns {Link[]}\n */\nexport function directoryLinks(doc, path = \"\") {\n const prefix = path ? `${path}/` : \"\";\n const dirs = new Set();\n const files = [];\n for (const [key, value] of Object.entries(doc ?? {})) {\n if (DIRECTORY_RESERVED.has(key) || !key.startsWith(prefix)) continue;\n const url = asUrl(value);\n if (!url) continue;\n const rest = key.slice(prefix.length).split(\"/\").filter(Boolean);\n if (rest.length === 0) continue;\n if (rest.length > 1) dirs.add(rest[0]);\n else files.push({ url, name: rest[0], type: fileType(rest[0]) });\n }\n const byName = (a, b) => a.name.localeCompare(b.name);\n return [\n ...[...dirs]\n .map((name) => ({ name, type: \"folder\", path: prefix + name }))\n .sort(byName),\n ...files.sort(byName),\n ];\n}\n"],
|
|
5
|
+
"mappings": ";AAAA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAWA,SAAS,SAAS,GAAG;AAC1B,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,kBAAkB,CAAC,EAAG,QAAO,OAAO,CAAC;AACzC,SAAO;AACT;AAEO,SAAS,MAAM,GAAG;AACvB,QAAM,IAAI,SAAS,CAAC;AACpB,SAAO,MAAM,UAAa,oBAAoB,CAAC,IAAI,IAAI;AACzD;AAKO,SAAS,eAAe,KAAK;AAClC,SAAO,SAAS,MAAM,YAAY,GAAG,IAAI,MAAM;AACjD;AAEA,IAAM,qBAAqB,oBAAI,IAAI,CAAC,cAAc,YAAY,CAAC;AAG/D,SAAS,SAAS,MAAM;AACtB,QAAM,MAAM,KAAK,YAAY,GAAG;AAChC,SAAO,MAAM,IAAI,KAAK,MAAM,MAAM,CAAC,IAAI;AACzC;AAGO,SAAS,YAAY,KAAK;AAC/B,QAAM,QAAQ,CAAC;AACf,aAAW,QAAQ,MAAM,QAAQ,KAAK,IAAI,IAAI,IAAI,OAAO,CAAC,GAAG;AAC3D,UAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,QAAI,CAAC,IAAK;AACV,UAAM,KAAK;AAAA,MACT;AAAA,MACA,MAAM,SAAS,KAAK,IAAI,KAAK;AAAA,MAC7B,MAAM,SAAS,KAAK,IAAI,KAAK;AAAA,IAC/B,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAOO,SAAS,eAAe,KAAK,OAAO,IAAI;AAC7C,QAAM,SAAS,OAAO,GAAG,IAAI,MAAM;AACnC,QAAM,OAAO,oBAAI,IAAI;AACrB,QAAM,QAAQ,CAAC;AACf,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,CAAC,CAAC,GAAG;AACpD,QAAI,mBAAmB,IAAI,GAAG,KAAK,CAAC,IAAI,WAAW,MAAM,EAAG;AAC5D,UAAM,MAAM,MAAM,KAAK;AACvB,QAAI,CAAC,IAAK;AACV,UAAM,OAAO,IAAI,MAAM,OAAO,MAAM,EAAE,MAAM,GAAG,EAAE,OAAO,OAAO;AAC/D,QAAI,KAAK,WAAW,EAAG;AACvB,QAAI,KAAK,SAAS,EAAG,MAAK,IAAI,KAAK,CAAC,CAAC;AAAA,QAChC,OAAM,KAAK,EAAE,KAAK,MAAM,KAAK,CAAC,GAAG,MAAM,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;AAAA,EACjE;AACA,QAAM,SAAS,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI;AACpD,SAAO;AAAA,IACL,GAAG,CAAC,GAAG,IAAI,EACR,IAAI,CAAC,UAAU,EAAE,MAAM,MAAM,UAAU,MAAM,SAAS,KAAK,EAAE,EAC7D,KAAK,MAAM;AAAA,IACd,GAAG,MAAM,KAAK,MAAM;AAAA,EACtB;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
asString
|
|
3
|
+
} from "./chunk-WN53YCSZ.js";
|
|
4
|
+
|
|
5
|
+
// src/datatype.js
|
|
6
|
+
var FolderDatatype = {
|
|
7
|
+
init(doc) {
|
|
8
|
+
doc.title = "New Folder";
|
|
9
|
+
doc.docs = [];
|
|
10
|
+
},
|
|
11
|
+
getTitle: (doc) => asString(doc.title) || "Unnamed Folder",
|
|
12
|
+
setTitle: (doc, title) => {
|
|
13
|
+
doc.title = title;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var DirectoryDatatype = {
|
|
17
|
+
init: () => {
|
|
18
|
+
throw new Error("Can't create an empty Directory");
|
|
19
|
+
},
|
|
20
|
+
getTitle: (doc) => asString(doc["@patchwork"]?.title) || "Unnamed Directory",
|
|
21
|
+
setTitle: (doc, title) => {
|
|
22
|
+
doc["@patchwork"].title = title;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var FileDatatype = {
|
|
26
|
+
init: () => {
|
|
27
|
+
throw new Error("Can't create empty File");
|
|
28
|
+
},
|
|
29
|
+
getTitle(doc) {
|
|
30
|
+
return asString(doc.name) || "Unnamed File";
|
|
31
|
+
},
|
|
32
|
+
setTitle(doc, title) {
|
|
33
|
+
doc.name = title;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
export {
|
|
37
|
+
DirectoryDatatype,
|
|
38
|
+
FileDatatype,
|
|
39
|
+
FolderDatatype
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=datatype-AGCTQZ52.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/datatype.js"],
|
|
4
|
+
"sourcesContent": ["import { asString } from './links.js';\n\nexport const FolderDatatype = {\n init(doc) {\n doc.title = 'New Folder';\n doc.docs = [];\n },\n getTitle: (doc) => asString(doc.title) || 'Unnamed Folder',\n setTitle: (doc, title) => {\n doc.title = title;\n },\n};\n\n// A pushwork directory doc: its title lives in its `@patchwork` metadata, and\n// its entries are made by pushwork, not here.\nexport const DirectoryDatatype = {\n init: () => {\n throw new Error(\"Can't create an empty Directory\");\n },\n getTitle: (doc) => asString(doc['@patchwork']?.title) || 'Unnamed Directory',\n setTitle: (doc, title) => {\n doc['@patchwork'].title = title;\n },\n};\n\nexport const FileDatatype = {\n init: () => {\n throw new Error(\"Can't create empty File\");\n },\n getTitle(doc) {\n return asString(doc.name) || 'Unnamed File';\n },\n setTitle(doc, title) {\n doc.name = title;\n },\n};\n"],
|
|
5
|
+
"mappings": ";;;;;AAEO,IAAM,iBAAiB;AAAA,EAC5B,KAAK,KAAK;AACR,QAAI,QAAQ;AACZ,QAAI,OAAO,CAAC;AAAA,EACd;AAAA,EACA,UAAU,CAAC,QAAQ,SAAS,IAAI,KAAK,KAAK;AAAA,EAC1C,UAAU,CAAC,KAAK,UAAU;AACxB,QAAI,QAAQ;AAAA,EACd;AACF;AAIO,IAAM,oBAAoB;AAAA,EAC/B,MAAM,MAAM;AACV,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACnD;AAAA,EACA,UAAU,CAAC,QAAQ,SAAS,IAAI,YAAY,GAAG,KAAK,KAAK;AAAA,EACzD,UAAU,CAAC,KAAK,UAAU;AACxB,QAAI,YAAY,EAAE,QAAQ;AAAA,EAC5B;AACF;AAEO,IAAM,eAAe;AAAA,EAC1B,MAAM,MAAM;AACV,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAAA,EACA,SAAS,KAAK;AACZ,WAAO,SAAS,IAAI,IAAI,KAAK;AAAA,EAC/B;AAAA,EACA,SAAS,KAAK,OAAO;AACnB,QAAI,OAAO;AAAA,EACb;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -6,17 +6,28 @@ var plugins = [
|
|
|
6
6
|
name: "Folder",
|
|
7
7
|
icon: "Folder",
|
|
8
8
|
async load() {
|
|
9
|
-
const { FolderDatatype } = await import("./datatype-
|
|
9
|
+
const { FolderDatatype } = await import("./datatype-AGCTQZ52.js");
|
|
10
10
|
return FolderDatatype;
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
+
{
|
|
14
|
+
type: "patchwork:datatype",
|
|
15
|
+
id: "directory",
|
|
16
|
+
name: "Directory",
|
|
17
|
+
icon: "Folder",
|
|
18
|
+
unlisted: true,
|
|
19
|
+
async load() {
|
|
20
|
+
const { DirectoryDatatype } = await import("./datatype-AGCTQZ52.js");
|
|
21
|
+
return DirectoryDatatype;
|
|
22
|
+
}
|
|
23
|
+
},
|
|
13
24
|
{
|
|
14
25
|
type: "patchwork:tool",
|
|
15
26
|
id: "folder-viewer",
|
|
16
27
|
name: "Folder Viewer",
|
|
17
|
-
supportedDatatypes: ["folder"],
|
|
28
|
+
supportedDatatypes: ["folder", "directory"],
|
|
18
29
|
async load() {
|
|
19
|
-
const { FolderTool } = await import("./tool-
|
|
30
|
+
const { FolderTool } = await import("./tool-JSQNX2I6.js");
|
|
20
31
|
return FolderTool;
|
|
21
32
|
}
|
|
22
33
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.js"],
|
|
4
|
-
"sourcesContent": ["export const plugins = [\n {\n type: 'patchwork:datatype',\n id: 'folder',\n name: 'Folder',\n icon: 'Folder',\n async load() {\n const { FolderDatatype } = await import('./datatype.js');\n return FolderDatatype;\n },\n },\n {\n type: 'patchwork:tool',\n id: 'folder-viewer',\n name: 'Folder Viewer',\n supportedDatatypes: ['folder'],\n async load() {\n const { FolderTool } = await import('./tool.js');\n return FolderTool;\n },\n }\n];\n"],
|
|
5
|
-
"mappings": ";AAAO,IAAM,UAAU;AAAA,EACrB;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM,OAAO;AACX,YAAM,EAAE,eAAe,IAAI,MAAM,OAAO,wBAAe;AACvD,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,oBAAoB,CAAC,
|
|
4
|
+
"sourcesContent": ["export const plugins = [\n {\n type: 'patchwork:datatype',\n id: 'folder',\n name: 'Folder',\n icon: 'Folder',\n async load() {\n const { FolderDatatype } = await import('./datatype.js');\n return FolderDatatype;\n },\n },\n {\n type: 'patchwork:datatype',\n id: 'directory',\n name: 'Directory',\n icon: 'Folder',\n unlisted: true,\n async load() {\n const { DirectoryDatatype } = await import('./datatype.js');\n return DirectoryDatatype;\n },\n },\n {\n type: 'patchwork:tool',\n id: 'folder-viewer',\n name: 'Folder Viewer',\n supportedDatatypes: ['folder', 'directory'],\n async load() {\n const { FolderTool } = await import('./tool.js');\n return FolderTool;\n },\n }\n];\n"],
|
|
5
|
+
"mappings": ";AAAO,IAAM,UAAU;AAAA,EACrB;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM,OAAO;AACX,YAAM,EAAE,eAAe,IAAI,MAAM,OAAO,wBAAe;AACvD,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA,IACV,MAAM,OAAO;AACX,YAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,wBAAe;AAC1D,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,oBAAoB,CAAC,UAAU,WAAW;AAAA,IAC1C,MAAM,OAAO;AACX,YAAM,EAAE,WAAW,IAAI,MAAM,OAAO,oBAAW;AAC/C,aAAO;AAAA,IACT;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
asString,
|
|
3
|
+
directoryLinks,
|
|
4
|
+
folderLinks,
|
|
5
|
+
isDirectoryDoc
|
|
6
|
+
} from "./chunk-WN53YCSZ.js";
|
|
7
|
+
|
|
1
8
|
// src/tool.js
|
|
2
9
|
import {
|
|
3
10
|
parseAutomergeUrl,
|
|
@@ -10,7 +17,7 @@ import {
|
|
|
10
17
|
} from "@inkandswitch/patchwork-plugins";
|
|
11
18
|
|
|
12
19
|
// src/styles.css
|
|
13
|
-
var styles_default = "@layer package {\n:root,\n:host,\n[theme] {\n /* The well the cards sit in \u2014 slightly recessed surface. */\n --folder-view-well: var(--editor-fill-offset-10, #f2f2f2);\n --folder-view-well-edge: var(--editor-fill-offset-30, #d6d6d6);\n --folder-view-fg: var(--editor-line, black);\n --folder-view-muted: var(--editor-line-offset-50, #999);\n --folder-view-border: var(--editor-fill-offset-20, #e5e5e5);\n\n /* A card = a raised surface sitting on the well. */\n --folder-view-card: var(--editor-fill, white);\n --folder-view-card-border: var(--editor-fill-offset-20, #e5e5e5);\n\n --folder-view-accent: var(--studio-primary-fill, var(--studio-primary, #35f7ca));\n --folder-view-accent-line: var(--studio-primary-line, var(--editor-fill, white));\n --folder-view-accent-text: var(--editor-primary-text, var(--studio-primary-text, var(--studio-primary, #35f7ca)));\n --folder-view-hover: var(--editor-fill-offset-10, #f2f2f2);\n\n /* Layered drop shadow so a card reads as a solid object resting on the\n well \u2014 a tight contact shadow plus a softer cast one. Derived from the\n theme line colour so it inverts in dark themes. */\n --folder-view-card-shadow: 0 1px 2px\n color-mix(in oklch, var(--editor-line, black), transparent 94%),\n 0 2px 8px color-mix(in oklch, var(--editor-line, black), transparent 96%);\n --folder-view-card-shadow-hover: 0 2px 4px\n color-mix(in oklch, var(--editor-line, black), transparent 90%),\n 0 8px 20px color-mix(in oklch, var(--editor-line, black), transparent 93%);\n\n --folder-view-family: var(--editor-family-sans, system-ui, sans-serif);\n}\n}\n\n/* Block: the whole tool. A sunken well with an inset edge. */\n.folder-view {\n height: 100%;\n overflow: auto;\n padding: var(--studio-space-md, 1rem);\n display: flex;\n flex-direction: column;\n gap: var(--studio-space-md, 1rem);\n font-family: var(--folder-view-family);\n color: var(--folder-view-fg);\n background: var(--folder-view-well);\n /* No outer border: the host frame already draws the lines around the pane, so\n the well reads as flush rather than double-edged. */\n}\n\n/* State: a document is being dragged in from the sidebar. Outline the well\n with the accent colour so it reads as an active drop target. */\n.folder-view[data-drop-active] {\n border-color: var(--folder-view-accent);\n box-shadow: inset 0 0 0 2px var(--folder-view-accent);\n}\n\n/* Composition: header row. New-doc button on the left, count on the right. */\n.folder-view .folder-view-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--studio-space-sm, 0.5rem);\n padding-bottom: var(--studio-space-sm, 0.5rem);\n border-bottom: 1px solid var(--folder-view-border);\n}\n\n/* Composition: the create-new control \u2014 a button anchoring a type-picker. */\n.folder-view .folder-create {\n position: relative;\n}\n\n.folder-view .folder-create-button {\n display: inline-flex;\n align-items: center;\n gap: var(--studio-space-2xs, 0.25rem);\n font: inherit;\n font-size: 0.8rem;\n color: var(--folder-view-accent-line);\n background: var(--folder-view-accent);\n border: 1px solid transparent;\n border-radius: var(--studio-radius-sm, 4px);\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n cursor: pointer;\n transition: filter var(--studio-transition-fast, 0.1s ease);\n}\n\n.folder-view .folder-create-button:hover {\n filter: brightness(0.96);\n}\n\n.folder-view .folder-create-icon {\n font-size: 1rem;\n line-height: 1;\n}\n\n/* The type-picker popover, anchored under the button. */\n.folder-view .folder-create-menu {\n position: absolute;\n top: calc(100% + var(--studio-space-2xs, 0.25rem));\n left: 0;\n z-index: 10;\n min-width: 12rem;\n max-height: 16rem;\n overflow: auto;\n display: flex;\n flex-direction: column;\n padding: var(--studio-space-3xs, 0.125rem);\n background: var(--folder-view-card);\n border: 1px solid var(--folder-view-card-border);\n border-radius: var(--studio-radius-md, 8px);\n box-shadow: var(--folder-view-card-shadow-hover);\n}\n\n.folder-view .folder-create-menu[hidden] {\n display: none;\n}\n\n.folder-view .folder-create-item {\n text-align: left;\n font: inherit;\n font-size: 0.85rem;\n color: var(--folder-view-fg);\n background: transparent;\n border: 0;\n border-radius: var(--studio-radius-sm, 4px);\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n cursor: pointer;\n}\n\n.folder-view .folder-create-item:hover {\n background: var(--folder-view-hover);\n}\n\n.folder-view .folder-create-empty {\n padding: var(--studio-space-sm, 0.5rem);\n font-size: 0.8rem;\n color: var(--folder-view-muted);\n}\n\n.folder-view .folder-view-count {\n font-size: 0.8rem;\n color: var(--folder-view-muted);\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n border-radius: var(--studio-radius-round, 9999px);\n background: var(--folder-view-card);\n border: 1px solid var(--folder-view-border);\n}\n\n/* Composition: the list of cards. */\n.folder-view .folder-view-list {\n display: flex;\n flex-direction: column;\n gap: var(--studio-space-md, 1rem);\n padding-bottom: var(--studio-space-md, 1rem);\n}\n\n.folder-view .folder-view-empty {\n text-align: center;\n color: var(--folder-view-muted);\n padding: var(--studio-space-xl, 2rem) 0;\n}\n\n.folder-view-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: var(--studio-space-md, 1rem);\n font-family: var(--folder-view-family);\n color: var(--folder-view-muted);\n background: var(--folder-view-card);\n}\n\n/* Block: one rendered file/folder card \u2014 sitting on the well. */\n.folder-view .folder-entry {\n background: var(--folder-view-card);\n border: 1px solid var(--folder-view-card-border);\n border-radius: var(--studio-radius-lg, 12px);\n box-shadow: var(--folder-view-card-shadow);\n overflow: hidden;\n transition: box-shadow var(--studio-transition, 0.15s ease),\n transform var(--studio-transition, 0.15s ease);\n}\n\n.folder-view .folder-entry:hover {\n box-shadow: var(--folder-view-card-shadow-hover);\n /* Lift slightly so the card feels like it peels up off the well. */\n transform: translateY(-1px);\n}\n\n.folder-view .folder-entry-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--studio-space-sm, 0.5rem);\n padding: var(--studio-space-sm, 0.5rem) var(--studio-space-md, 1rem);\n}\n\n.folder-view .folder-entry-title {\n display: flex;\n align-items: baseline;\n gap: var(--studio-space-sm, 0.5rem);\n min-width: 0;\n}\n\n.folder-view .folder-entry-name {\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.folder-view .folder-entry-type {\n font-size: 0.7rem;\n color: var(--folder-view-muted);\n padding: var(--studio-space-3xs, 0.125rem) var(--studio-space-2xs, 0.25rem);\n border: 1px solid var(--folder-view-border);\n border-radius: var(--studio-radius-sm, 4px);\n flex-shrink: 0;\n}\n\n.folder-view .folder-entry-open {\n font-size: 0.8rem;\n color: var(--folder-view-accent-text);\n text-decoration: none;\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n border-radius: var(--studio-radius-sm, 4px);\n flex-shrink: 0;\n transition: background var(--studio-transition-fast, 0.1s ease);\n}\n\n.folder-view .folder-entry-open:hover {\n background: var(--folder-view-hover);\n}\n\n.folder-view .folder-entry-hint {\n margin: 0;\n padding: 0 var(--studio-space-md, 1rem) var(--studio-space-md, 1rem);\n font-size: 0.85rem;\n color: var(--folder-view-muted);\n}\n\n/* Block: embedded preview body. */\n.folder-view .folder-entry-body {\n flex: 1 1 auto;\n min-height: 0;\n max-height: 300px;\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--folder-view-border);\n}\n\n.folder-view .folder-entry-scroll {\n height: 100%;\n overflow: auto;\n}\n\n.folder-view .folder-entry-scroll patchwork-view {\n display: block;\n height: 100%;\n}\n";
|
|
20
|
+
var styles_default = "@layer package {\n:root,\n:host,\n[theme] {\n /* The well the cards sit in \u2014 slightly recessed surface. */\n --folder-view-well: var(--editor-fill-offset-10, #f2f2f2);\n --folder-view-well-edge: var(--editor-fill-offset-30, #d6d6d6);\n --folder-view-fg: var(--editor-line, black);\n --folder-view-muted: var(--editor-line-offset-50, #999);\n --folder-view-border: var(--editor-fill-offset-20, #e5e5e5);\n\n /* A card = a raised surface sitting on the well. */\n --folder-view-card: var(--editor-fill, white);\n --folder-view-card-border: var(--editor-fill-offset-20, #e5e5e5);\n\n --folder-view-accent: var(--studio-primary-fill, var(--studio-primary, #35f7ca));\n --folder-view-accent-line: var(--studio-primary-line, var(--editor-fill, white));\n --folder-view-accent-text: var(--editor-primary-text, var(--studio-primary-text, var(--studio-primary, #35f7ca)));\n --folder-view-hover: var(--editor-fill-offset-10, #f2f2f2);\n\n /* Layered drop shadow so a card reads as a solid object resting on the\n well \u2014 a tight contact shadow plus a softer cast one. Derived from the\n theme line colour so it inverts in dark themes. */\n --folder-view-card-shadow: 0 1px 2px\n color-mix(in oklch, var(--editor-line, black), transparent 94%),\n 0 2px 8px color-mix(in oklch, var(--editor-line, black), transparent 96%);\n --folder-view-card-shadow-hover: 0 2px 4px\n color-mix(in oklch, var(--editor-line, black), transparent 90%),\n 0 8px 20px color-mix(in oklch, var(--editor-line, black), transparent 93%);\n\n --folder-view-family: var(--editor-family-sans, system-ui, sans-serif);\n}\n}\n\n/* Block: the whole tool. A sunken well with an inset edge. */\n.folder-view {\n height: 100%;\n overflow: auto;\n padding: var(--studio-space-md, 1rem);\n display: flex;\n flex-direction: column;\n gap: var(--studio-space-md, 1rem);\n font-family: var(--folder-view-family);\n color: var(--folder-view-fg);\n background: var(--folder-view-well);\n /* No outer border: the host frame already draws the lines around the pane, so\n the well reads as flush rather than double-edged. */\n}\n\n/* State: a document is being dragged in from the sidebar. Outline the well\n with the accent colour so it reads as an active drop target. */\n.folder-view[data-drop-active] {\n border-color: var(--folder-view-accent);\n box-shadow: inset 0 0 0 2px var(--folder-view-accent);\n}\n\n/* Composition: header row. New-doc button on the left, count on the right. */\n.folder-view .folder-view-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--studio-space-sm, 0.5rem);\n padding-bottom: var(--studio-space-sm, 0.5rem);\n border-bottom: 1px solid var(--folder-view-border);\n}\n\n/* Composition: where we are inside a pushwork directory doc, in place of the\n create-new control (pushwork owns those entries). */\n.folder-view .folder-view-crumbs:not([hidden]) {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--studio-space-2xs, 0.25rem);\n min-width: 0;\n font-size: 0.85rem;\n}\n\n.folder-view .folder-view-crumb {\n font: inherit;\n color: var(--folder-view-fg);\n background: none;\n border: none;\n padding: var(--studio-space-2xs, 0.25rem);\n border-radius: var(--studio-radius-sm, 4px);\n}\n\n.folder-view button.folder-view-crumb {\n color: var(--folder-view-accent-text);\n cursor: pointer;\n}\n\n.folder-view button.folder-view-crumb:hover {\n background: var(--folder-view-hover);\n}\n\n.folder-view .folder-view-crumb-sep {\n color: var(--folder-view-muted);\n}\n\n/* Composition: the create-new control \u2014 a button anchoring a type-picker. */\n.folder-view .folder-create {\n position: relative;\n}\n\n.folder-view .folder-create-button {\n display: inline-flex;\n align-items: center;\n gap: var(--studio-space-2xs, 0.25rem);\n font: inherit;\n font-size: 0.8rem;\n color: var(--folder-view-accent-line);\n background: var(--folder-view-accent);\n border: 1px solid transparent;\n border-radius: var(--studio-radius-sm, 4px);\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n cursor: pointer;\n transition: filter var(--studio-transition-fast, 0.1s ease);\n}\n\n.folder-view .folder-create-button:hover {\n filter: brightness(0.96);\n}\n\n.folder-view .folder-create-icon {\n font-size: 1rem;\n line-height: 1;\n}\n\n/* The type-picker popover, anchored under the button. */\n.folder-view .folder-create-menu {\n position: absolute;\n top: calc(100% + var(--studio-space-2xs, 0.25rem));\n left: 0;\n z-index: 10;\n min-width: 12rem;\n max-height: 16rem;\n overflow: auto;\n display: flex;\n flex-direction: column;\n padding: var(--studio-space-3xs, 0.125rem);\n background: var(--folder-view-card);\n border: 1px solid var(--folder-view-card-border);\n border-radius: var(--studio-radius-md, 8px);\n box-shadow: var(--folder-view-card-shadow-hover);\n}\n\n.folder-view .folder-create-menu[hidden] {\n display: none;\n}\n\n.folder-view .folder-create-item {\n text-align: left;\n font: inherit;\n font-size: 0.85rem;\n color: var(--folder-view-fg);\n background: transparent;\n border: 0;\n border-radius: var(--studio-radius-sm, 4px);\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n cursor: pointer;\n}\n\n.folder-view .folder-create-item:hover {\n background: var(--folder-view-hover);\n}\n\n.folder-view .folder-create-empty {\n padding: var(--studio-space-sm, 0.5rem);\n font-size: 0.8rem;\n color: var(--folder-view-muted);\n}\n\n.folder-view .folder-view-count {\n font-size: 0.8rem;\n color: var(--folder-view-muted);\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n border-radius: var(--studio-radius-round, 9999px);\n background: var(--folder-view-card);\n border: 1px solid var(--folder-view-border);\n}\n\n/* Composition: the list of cards. */\n.folder-view .folder-view-list {\n display: flex;\n flex-direction: column;\n gap: var(--studio-space-md, 1rem);\n padding-bottom: var(--studio-space-md, 1rem);\n}\n\n.folder-view .folder-view-empty {\n text-align: center;\n color: var(--folder-view-muted);\n padding: var(--studio-space-xl, 2rem) 0;\n}\n\n.folder-view-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: var(--studio-space-md, 1rem);\n font-family: var(--folder-view-family);\n color: var(--folder-view-muted);\n background: var(--folder-view-card);\n}\n\n/* Block: one rendered file/folder card \u2014 sitting on the well. */\n.folder-view .folder-entry {\n background: var(--folder-view-card);\n border: 1px solid var(--folder-view-card-border);\n border-radius: var(--studio-radius-lg, 12px);\n box-shadow: var(--folder-view-card-shadow);\n overflow: hidden;\n transition: box-shadow var(--studio-transition, 0.15s ease),\n transform var(--studio-transition, 0.15s ease);\n}\n\n.folder-view .folder-entry:hover {\n box-shadow: var(--folder-view-card-shadow-hover);\n /* Lift slightly so the card feels like it peels up off the well. */\n transform: translateY(-1px);\n}\n\n.folder-view .folder-entry-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--studio-space-sm, 0.5rem);\n padding: var(--studio-space-sm, 0.5rem) var(--studio-space-md, 1rem);\n}\n\n.folder-view .folder-entry-title {\n display: flex;\n align-items: baseline;\n gap: var(--studio-space-sm, 0.5rem);\n min-width: 0;\n}\n\n.folder-view .folder-entry-name {\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.folder-view .folder-entry-type {\n font-size: 0.7rem;\n color: var(--folder-view-muted);\n padding: var(--studio-space-3xs, 0.125rem) var(--studio-space-2xs, 0.25rem);\n border: 1px solid var(--folder-view-border);\n border-radius: var(--studio-radius-sm, 4px);\n flex-shrink: 0;\n}\n\n.folder-view .folder-entry-open {\n font: inherit;\n font-size: 0.8rem;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--folder-view-accent-text);\n text-decoration: none;\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n border-radius: var(--studio-radius-sm, 4px);\n flex-shrink: 0;\n transition: background var(--studio-transition-fast, 0.1s ease);\n}\n\n.folder-view .folder-entry-open:hover {\n background: var(--folder-view-hover);\n}\n\n.folder-view .folder-entry-hint {\n margin: 0;\n padding: 0 var(--studio-space-md, 1rem) var(--studio-space-md, 1rem);\n font-size: 0.85rem;\n color: var(--folder-view-muted);\n}\n\n/* Block: embedded preview body. */\n.folder-view .folder-entry-body {\n flex: 1 1 auto;\n min-height: 0;\n max-height: 300px;\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--folder-view-border);\n}\n\n.folder-view .folder-entry-scroll {\n height: 100%;\n overflow: auto;\n}\n\n.folder-view .folder-entry-scroll patchwork-view {\n display: block;\n height: 100%;\n}\n";
|
|
14
21
|
|
|
15
22
|
// src/tool.js
|
|
16
23
|
var DND_DATA_TYPES = [
|
|
@@ -83,17 +90,28 @@ function el(tag, attrs, ...children) {
|
|
|
83
90
|
function entryHref(docLink) {
|
|
84
91
|
return `#doc=${parseAutomergeUrl(docLink.url).documentId}&type=${docLink.type}`;
|
|
85
92
|
}
|
|
93
|
+
function entryKey(docLink) {
|
|
94
|
+
return docLink.url ?? `path:${docLink.path}`;
|
|
95
|
+
}
|
|
86
96
|
function renderLoading() {
|
|
87
97
|
return el("div", { className: "folder-view-loading" }, "Loading\u2026");
|
|
88
98
|
}
|
|
89
|
-
function buildEntry(docLink) {
|
|
99
|
+
function buildEntry(docLink, enter) {
|
|
90
100
|
const isFolder = docLink.type === "folder";
|
|
91
101
|
const nameEl = el("span", { className: "folder-entry-name" }, docLink.name);
|
|
92
102
|
const typeEl = el("span", { className: "folder-entry-type" }, docLink.type);
|
|
93
|
-
const openEl = el(
|
|
103
|
+
const openEl = docLink.url ? el(
|
|
94
104
|
"a",
|
|
95
105
|
{ className: "folder-entry-open", href: entryHref(docLink) },
|
|
96
106
|
"Open"
|
|
107
|
+
) : el(
|
|
108
|
+
"button",
|
|
109
|
+
{
|
|
110
|
+
className: "folder-entry-open",
|
|
111
|
+
type: "button",
|
|
112
|
+
onClick: () => enter(docLink.path)
|
|
113
|
+
},
|
|
114
|
+
"Open"
|
|
97
115
|
);
|
|
98
116
|
const node = el(
|
|
99
117
|
"div",
|
|
@@ -137,6 +155,7 @@ function updateEntry(entry, docLink) {
|
|
|
137
155
|
if (entry.node.getAttribute("data-type") !== docLink.type) {
|
|
138
156
|
entry.node.setAttribute("data-type", docLink.type);
|
|
139
157
|
}
|
|
158
|
+
if (!docLink.url) return;
|
|
140
159
|
const href = entryHref(docLink);
|
|
141
160
|
if (entry.openEl.getAttribute("href") !== href) {
|
|
142
161
|
entry.openEl.setAttribute("href", href);
|
|
@@ -242,8 +261,14 @@ var FolderTool = (handle, element) => {
|
|
|
242
261
|
const entries = /* @__PURE__ */ new Map();
|
|
243
262
|
const styleEl = el("style");
|
|
244
263
|
styleEl.textContent = styles_default;
|
|
264
|
+
let path = "";
|
|
265
|
+
function enter(next) {
|
|
266
|
+
path = next;
|
|
267
|
+
render();
|
|
268
|
+
}
|
|
245
269
|
const countEl = el("span", { className: "folder-view-count" });
|
|
246
270
|
const listEl = el("div", { className: "folder-view-list" });
|
|
271
|
+
const crumbsEl = el("nav", { className: "folder-view-crumbs", hidden: "" });
|
|
247
272
|
const createNew = buildCreateNew(handle, element);
|
|
248
273
|
const shell = el(
|
|
249
274
|
"div",
|
|
@@ -252,10 +277,32 @@ var FolderTool = (handle, element) => {
|
|
|
252
277
|
"div",
|
|
253
278
|
{ className: "folder-view-header" },
|
|
254
279
|
createNew.node,
|
|
280
|
+
crumbsEl,
|
|
255
281
|
countEl
|
|
256
282
|
),
|
|
257
283
|
listEl
|
|
258
284
|
);
|
|
285
|
+
function renderCrumbs(title) {
|
|
286
|
+
const parts = path ? path.split("/") : [];
|
|
287
|
+
const crumbs = [{ name: title, path: "" }].concat(
|
|
288
|
+
parts.map((name, i) => ({ name, path: parts.slice(0, i + 1).join("/") }))
|
|
289
|
+
);
|
|
290
|
+
crumbsEl.replaceChildren(
|
|
291
|
+
...crumbs.flatMap((crumb, i) => {
|
|
292
|
+
const last = i === crumbs.length - 1;
|
|
293
|
+
const node = last ? el("span", { className: "folder-view-crumb" }, crumb.name) : el(
|
|
294
|
+
"button",
|
|
295
|
+
{
|
|
296
|
+
className: "folder-view-crumb",
|
|
297
|
+
type: "button",
|
|
298
|
+
onClick: () => enter(crumb.path)
|
|
299
|
+
},
|
|
300
|
+
crumb.name
|
|
301
|
+
);
|
|
302
|
+
return i === 0 ? [node] : [el("span", { className: "folder-view-crumb-sep" }, "/"), node];
|
|
303
|
+
})
|
|
304
|
+
);
|
|
305
|
+
}
|
|
259
306
|
element.append(styleEl);
|
|
260
307
|
let dragDepth = 0;
|
|
261
308
|
function endDrag() {
|
|
@@ -264,8 +311,8 @@ var FolderTool = (handle, element) => {
|
|
|
264
311
|
}
|
|
265
312
|
function addDroppedDocs(event) {
|
|
266
313
|
const folder = handle.doc();
|
|
267
|
-
if (!folder) return;
|
|
268
|
-
const existing = new Set(folder.
|
|
314
|
+
if (!folder || isDirectoryDoc(folder)) return;
|
|
315
|
+
const existing = new Set(folderLinks(folder).map((docLink) => docLink.url));
|
|
269
316
|
const selfUrl = handle.url;
|
|
270
317
|
const links = [];
|
|
271
318
|
for (const item of getDndItems(event)) {
|
|
@@ -280,27 +327,31 @@ var FolderTool = (handle, element) => {
|
|
|
280
327
|
}
|
|
281
328
|
if (links.length === 0) return;
|
|
282
329
|
handle.change((doc) => {
|
|
330
|
+
if (!Array.isArray(doc.docs)) doc.docs = [];
|
|
283
331
|
doc.docs.push(...links);
|
|
284
332
|
});
|
|
285
333
|
}
|
|
334
|
+
function acceptsDrops(event) {
|
|
335
|
+
return hasDocumentDrag(event.dataTransfer) && !isDirectoryDoc(handle.doc());
|
|
336
|
+
}
|
|
286
337
|
shell.addEventListener("dragenter", (event) => {
|
|
287
|
-
if (!
|
|
338
|
+
if (!acceptsDrops(event)) return;
|
|
288
339
|
event.preventDefault();
|
|
289
340
|
dragDepth++;
|
|
290
341
|
shell.setAttribute("data-drop-active", "");
|
|
291
342
|
});
|
|
292
343
|
shell.addEventListener("dragover", (event) => {
|
|
293
|
-
if (!
|
|
344
|
+
if (!acceptsDrops(event)) return;
|
|
294
345
|
event.preventDefault();
|
|
295
346
|
event.dataTransfer.dropEffect = "link";
|
|
296
347
|
});
|
|
297
348
|
shell.addEventListener("dragleave", (event) => {
|
|
298
|
-
if (!
|
|
349
|
+
if (!acceptsDrops(event)) return;
|
|
299
350
|
dragDepth--;
|
|
300
351
|
if (dragDepth <= 0) endDrag();
|
|
301
352
|
});
|
|
302
353
|
shell.addEventListener("drop", (event) => {
|
|
303
|
-
if (!
|
|
354
|
+
if (!acceptsDrops(event)) return;
|
|
304
355
|
event.preventDefault();
|
|
305
356
|
endDrag();
|
|
306
357
|
addDroppedDocs(event);
|
|
@@ -320,8 +371,21 @@ var FolderTool = (handle, element) => {
|
|
|
320
371
|
return;
|
|
321
372
|
}
|
|
322
373
|
show(shell);
|
|
323
|
-
|
|
324
|
-
|
|
374
|
+
const directory = isDirectoryDoc(folder);
|
|
375
|
+
let links;
|
|
376
|
+
if (directory) {
|
|
377
|
+
links = directoryLinks(folder, path);
|
|
378
|
+
renderCrumbs(
|
|
379
|
+
asString(folder["@patchwork"].title) || "Directory"
|
|
380
|
+
);
|
|
381
|
+
} else {
|
|
382
|
+
path = "";
|
|
383
|
+
links = folderLinks(folder);
|
|
384
|
+
}
|
|
385
|
+
createNew.node.hidden = directory;
|
|
386
|
+
crumbsEl.hidden = !directory;
|
|
387
|
+
countEl.textContent = `${links.length} ${links.length === 1 ? "document" : "documents"}`;
|
|
388
|
+
if (links.length === 0) {
|
|
325
389
|
entries.clear();
|
|
326
390
|
listEl.replaceChildren(
|
|
327
391
|
el("div", { className: "folder-view-empty" }, "This folder is empty")
|
|
@@ -330,21 +394,22 @@ var FolderTool = (handle, element) => {
|
|
|
330
394
|
}
|
|
331
395
|
const seen = /* @__PURE__ */ new Set();
|
|
332
396
|
const ordered = [];
|
|
333
|
-
for (const docLink of
|
|
334
|
-
|
|
397
|
+
for (const docLink of links) {
|
|
398
|
+
const key = entryKey(docLink);
|
|
399
|
+
seen.add(key);
|
|
335
400
|
const isFolder = docLink.type === "folder";
|
|
336
|
-
let entry = entries.get(
|
|
401
|
+
let entry = entries.get(key);
|
|
337
402
|
if (!entry || entry.isFolder !== isFolder) {
|
|
338
|
-
entry = buildEntry(docLink);
|
|
339
|
-
entries.set(
|
|
403
|
+
entry = buildEntry(docLink, enter);
|
|
404
|
+
entries.set(key, entry);
|
|
340
405
|
} else {
|
|
341
406
|
updateEntry(entry, docLink);
|
|
342
407
|
}
|
|
343
408
|
ordered.push(entry.node);
|
|
344
409
|
}
|
|
345
|
-
for (const
|
|
346
|
-
if (seen.has(
|
|
347
|
-
entries.delete(
|
|
410
|
+
for (const key of [...entries.keys()]) {
|
|
411
|
+
if (seen.has(key)) continue;
|
|
412
|
+
entries.delete(key);
|
|
348
413
|
}
|
|
349
414
|
listEl.replaceChildren(...ordered);
|
|
350
415
|
}
|
|
@@ -362,4 +427,4 @@ var FolderTool = (handle, element) => {
|
|
|
362
427
|
export {
|
|
363
428
|
FolderTool
|
|
364
429
|
};
|
|
365
|
-
//# sourceMappingURL=tool-
|
|
430
|
+
//# sourceMappingURL=tool-JSQNX2I6.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/tool.js", "../src/styles.css"],
|
|
4
|
+
"sourcesContent": ["import {\n parseAutomergeUrl,\n isValidAutomergeUrl,\n} from \"@automerge/automerge-repo/slim\";\nimport { openDocument } from \"@inkandswitch/patchwork-elements\";\nimport {\n getRegistry,\n createDocOfDatatype2,\n} from \"@inkandswitch/patchwork-plugins\";\nimport {\n asString,\n directoryLinks,\n folderLinks,\n isDirectoryDoc,\n} from \"./links.js\";\nimport styles from \"./styles.css\";\n\n// MIME types we can extract document drags from, in order of preference.\n// These mirror what the sideboard sets on dragstart so dropping a sidebar\n// item into the folder view adds it to the folder.\nconst DND_DATA_TYPES = [\n \"text/x-patchwork-dnd\",\n \"text/x-patchwork-urls\",\n \"text/uri-list\",\n \"text/plain\",\n];\n\nfunction hasDocumentDrag(dataTransfer) {\n return Boolean(\n dataTransfer &&\n DND_DATA_TYPES.some((type) => dataTransfer.types.includes(type))\n );\n}\n\nfunction urlFromText(text) {\n const trimmed = text.trim();\n if (isValidAutomergeUrl(trimmed)) return trimmed;\n // patchwork web links carry the document id in the fragment: #doc=<documentId>\n const docId = trimmed.match(/#doc=([^&\\s]+)/)?.[1];\n if (docId && isValidAutomergeUrl(`automerge:${docId}`)) {\n return `automerge:${docId}`;\n }\n return null;\n}\n\n// Extract the dragged documents from a drop event. Returns an array of\n// { url, name?, type? } items, or an empty array if there's nothing droppable.\nfunction getDndItems(event) {\n const data = event.dataTransfer;\n if (!data) return [];\n\n const dndData = data.getData(\"text/x-patchwork-dnd\");\n if (dndData) {\n try {\n const parsed = JSON.parse(dndData);\n if (Array.isArray(parsed?.items) && parsed.items.length > 0) {\n return parsed.items.filter((item) => isValidAutomergeUrl(item?.url));\n }\n } catch {\n // fall through to the other types\n }\n }\n\n const urlData = data.getData(\"text/x-patchwork-urls\");\n if (urlData) {\n try {\n const urls = JSON.parse(urlData);\n const items = (Array.isArray(urls) ? urls : [])\n .filter((url) => isValidAutomergeUrl(url))\n .map((url) => ({ url }));\n if (items.length > 0) return items;\n } catch {\n // fall through to the other types\n }\n }\n\n const text = data.getData(\"text/uri-list\") || data.getData(\"text/plain\");\n return text\n .split(/\\r?\\n/)\n .map(urlFromText)\n .filter((url) => url !== null)\n .map((url) => ({ url }));\n}\n\nfunction el(tag, attrs, ...children) {\n const node = document.createElement(tag);\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (value == null || value === false) continue;\n if (key === \"className\") {\n node.className = value;\n } else if (key.startsWith(\"on\") && typeof value === \"function\") {\n node.addEventListener(key.slice(2).toLowerCase(), value);\n } else {\n node.setAttribute(key, value);\n }\n }\n }\n for (const child of children.flat()) {\n if (child == null || child === false) continue;\n node.appendChild(\n typeof child === \"string\" ? document.createTextNode(child) : child\n );\n }\n return node;\n}\n\nfunction entryHref(docLink) {\n return `#doc=${parseAutomergeUrl(docLink.url).documentId}&type=${docLink.type}`;\n}\n\n// A link is keyed by its doc, or, for a pushwork subdirectory with no doc of\n// its own, by its path.\nfunction entryKey(docLink) {\n return docLink.url ?? `path:${docLink.path}`;\n}\n\nfunction renderLoading() {\n return el(\"div\", { className: \"folder-view-loading\" }, \"Loading\u2026\");\n}\n\n// `enter(path)` opens a pushwork subdirectory in place.\nfunction buildEntry(docLink, enter) {\n const isFolder = docLink.type === \"folder\";\n const nameEl = el(\"span\", { className: \"folder-entry-name\" }, docLink.name);\n const typeEl = el(\"span\", { className: \"folder-entry-type\" }, docLink.type);\n const openEl = docLink.url\n ? el(\n \"a\",\n { className: \"folder-entry-open\", href: entryHref(docLink) },\n \"Open\"\n )\n : el(\n \"button\",\n {\n className: \"folder-entry-open\",\n type: \"button\",\n onClick: () => enter(docLink.path),\n },\n \"Open\"\n );\n\n // The nested view names the doc without heads; resolution (the drafts\n // `repo:handle-descriptor` answer) pins it to the active checkpoint when one\n // is checked out, so the entry freezes with the folder it lives in.\n const node = el(\n \"div\",\n { className: \"folder-entry\", \"data-type\": docLink.type },\n el(\n \"div\",\n { className: \"folder-entry-head\" },\n el(\"div\", { className: \"folder-entry-title\" }, nameEl, typeEl),\n openEl\n ),\n isFolder\n ? el(\n \"p\",\n { className: \"folder-entry-hint\" },\n 'Click \"Open\" to view folder contents'\n )\n : el(\n \"div\",\n { className: \"folder-entry-body\" },\n el(\n \"div\",\n { className: \"folder-entry-scroll\" },\n el(\"patchwork-view\", { \"doc-url\": docLink.url })\n )\n )\n );\n\n return {\n node,\n nameEl,\n typeEl,\n openEl,\n isFolder,\n url: docLink.url,\n };\n}\n\nfunction updateEntry(entry, docLink) {\n if (entry.nameEl.textContent !== docLink.name) {\n entry.nameEl.textContent = docLink.name;\n }\n if (entry.typeEl.textContent !== docLink.type) {\n entry.typeEl.textContent = docLink.type;\n }\n if (entry.node.getAttribute(\"data-type\") !== docLink.type) {\n entry.node.setAttribute(\"data-type\", docLink.type);\n }\n if (!docLink.url) return;\n const href = entryHref(docLink);\n if (entry.openEl.getAttribute(\"href\") !== href) {\n entry.openEl.setAttribute(\"href\", href);\n }\n}\n\n// The listable document types, sorted by name. `file` and other unlisted\n// datatypes are hidden \u2014 you can't create an empty File, for instance.\nfunction listableDatatypes() {\n return getRegistry(\"patchwork:datatype\")\n .all()\n .filter((datatype) => !datatype.unlisted)\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n\n// Create a fresh document of the given datatype and return a DocLink for it.\n// Mirrors the sideboard's createNew: load the plugin on demand, create the doc,\n// then read its title.\nasync function createDocLink(repo, datatype) {\n const loaded = await getRegistry(\"patchwork:datatype\").load(datatype.id);\n if (!loaded) throw new Error(`couldn't load datatype \"${datatype.id}\"`);\n const docHandle = await createDocOfDatatype2(loaded, repo);\n return {\n url: docHandle.url,\n name: loaded.module.getTitle(docHandle.doc()),\n type: datatype.id,\n };\n}\n\n// The \"New\" button plus its type-picker popover. Creating a doc appends a\n// DocLink to the folder and opens it. Returns { node, dispose }.\nfunction buildCreateNew(handle, element) {\n const menu = el(\"div\", { className: \"folder-create-menu\", hidden: \"\" });\n const button = el(\n \"button\",\n {\n className: \"folder-create-button\",\n type: \"button\",\n \"aria-label\": \"Create new document\",\n },\n el(\"span\", { className: \"folder-create-icon\" }, \"+\"),\n \"New\"\n );\n const node = el(\"div\", { className: \"folder-create\" }, button, menu);\n\n let open = false;\n\n function setOpen(next) {\n open = next;\n if (open) {\n renderMenu();\n menu.removeAttribute(\"hidden\");\n node.setAttribute(\"data-open\", \"\");\n } else {\n menu.setAttribute(\"hidden\", \"\");\n node.removeAttribute(\"data-open\");\n }\n }\n\n function renderMenu() {\n const datatypes = listableDatatypes();\n if (datatypes.length === 0) {\n menu.replaceChildren(\n el(\n \"div\",\n { className: \"folder-create-empty\" },\n \"No document types available\"\n )\n );\n return;\n }\n menu.replaceChildren(\n ...datatypes.map((datatype) =>\n el(\n \"button\",\n {\n className: \"folder-create-item\",\n type: \"button\",\n onClick: () => pick(datatype),\n },\n datatype.name\n )\n )\n );\n }\n\n async function pick(datatype) {\n setOpen(false);\n const repo = element.repo;\n if (!repo) return;\n try {\n const link = await createDocLink(repo, datatype);\n handle.change((doc) => {\n doc.docs.push(link);\n });\n openDocument(element, link.url);\n } catch (error) {\n console.error(\"folder: couldn't create document\", error);\n }\n }\n\n button.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n setOpen(!open);\n });\n\n // Dismiss the menu on an outside click or Escape.\n const onPointerDown = (event) => {\n if (open && !node.contains(event.target)) setOpen(false);\n };\n const onKeyDown = (event) => {\n if (open && event.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(\"pointerdown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n\n const dispose = () => {\n document.removeEventListener(\"pointerdown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n\n return { node, dispose };\n}\n\nexport const FolderTool = (handle, element) => {\n const entries = new Map();\n\n const styleEl = el(\"style\");\n styleEl.textContent = styles;\n\n // Where we are inside a pushwork directory doc, which keeps its whole tree\n // in one doc. \"\" is the top.\n let path = \"\";\n\n function enter(next) {\n path = next;\n render();\n }\n\n const countEl = el(\"span\", { className: \"folder-view-count\" });\n const listEl = el(\"div\", { className: \"folder-view-list\" });\n const crumbsEl = el(\"nav\", { className: \"folder-view-crumbs\", hidden: \"\" });\n const createNew = buildCreateNew(handle, element);\n const shell = el(\n \"div\",\n { className: \"folder-view\" },\n el(\n \"div\",\n { className: \"folder-view-header\" },\n createNew.node,\n crumbsEl,\n countEl\n ),\n listEl\n );\n\n function renderCrumbs(title) {\n const parts = path ? path.split(\"/\") : [];\n const crumbs = [{ name: title, path: \"\" }].concat(\n parts.map((name, i) => ({ name, path: parts.slice(0, i + 1).join(\"/\") }))\n );\n crumbsEl.replaceChildren(\n ...crumbs.flatMap((crumb, i) => {\n const last = i === crumbs.length - 1;\n const node = last\n ? el(\"span\", { className: \"folder-view-crumb\" }, crumb.name)\n : el(\n \"button\",\n {\n className: \"folder-view-crumb\",\n type: \"button\",\n onClick: () => enter(crumb.path),\n },\n crumb.name\n );\n return i === 0\n ? [node]\n : [el(\"span\", { className: \"folder-view-crumb-sep\" }, \"/\"), node];\n })\n );\n }\n\n element.append(styleEl);\n\n // --- drag-and-drop: accept documents dragged in from the sidebar ---\n\n // dragenter/dragleave fire for every descendant, so track nesting depth to\n // know when the pointer has actually left the folder view.\n let dragDepth = 0;\n\n function endDrag() {\n dragDepth = 0;\n shell.removeAttribute(\"data-drop-active\");\n }\n\n function addDroppedDocs(event) {\n const folder = handle.doc();\n if (!folder || isDirectoryDoc(folder)) return;\n\n const existing = new Set(folderLinks(folder).map((docLink) => docLink.url));\n const selfUrl = handle.url;\n\n const links = [];\n for (const item of getDndItems(event)) {\n if (item.url === selfUrl) continue; // don't nest a folder inside itself\n if (existing.has(item.url)) continue; // already here\n existing.add(item.url); // de-dupe within a single drop too\n links.push({\n url: item.url,\n name: item.name || \"Untitled\",\n type: item.type || \"\",\n });\n }\n\n if (links.length === 0) return;\n\n handle.change((doc) => {\n if (!Array.isArray(doc.docs)) doc.docs = [];\n doc.docs.push(...links);\n });\n }\n\n // pushwork owns a directory doc's entries, so it's read-only here.\n function acceptsDrops(event) {\n return hasDocumentDrag(event.dataTransfer) && !isDirectoryDoc(handle.doc());\n }\n\n shell.addEventListener(\"dragenter\", (event) => {\n if (!acceptsDrops(event)) return;\n event.preventDefault();\n dragDepth++;\n shell.setAttribute(\"data-drop-active\", \"\");\n });\n\n shell.addEventListener(\"dragover\", (event) => {\n if (!acceptsDrops(event)) return;\n event.preventDefault();\n // \"link\": dropping here adds a new DocLink to the same automerge url \u2014 the\n // doc isn't moved or cloned. Requires the source's effectAllowed to permit\n // link (the sideboard sets \"all\").\n event.dataTransfer.dropEffect = \"link\";\n });\n\n shell.addEventListener(\"dragleave\", (event) => {\n if (!acceptsDrops(event)) return;\n dragDepth--;\n if (dragDepth <= 0) endDrag();\n });\n\n shell.addEventListener(\"drop\", (event) => {\n if (!acceptsDrops(event)) return;\n event.preventDefault();\n endDrag();\n addDroppedDocs(event);\n });\n\n let mounted = null;\n\n function show(node) {\n if (mounted !== node) {\n // keep the injected <style>, swap the rendered tree\n if (mounted) mounted.remove();\n element.append(node);\n mounted = node;\n }\n }\n\n function render() {\n const folder = handle.doc();\n if (!folder) {\n show(renderLoading());\n return;\n }\n\n show(shell);\n\n const directory = isDirectoryDoc(folder);\n let links;\n if (directory) {\n links = directoryLinks(folder, path);\n renderCrumbs(\n asString(folder[\"@patchwork\"].title) || \"Directory\"\n );\n } else {\n path = \"\";\n links = folderLinks(folder);\n }\n createNew.node.hidden = directory;\n crumbsEl.hidden = !directory;\n\n countEl.textContent = `${links.length} ${\n links.length === 1 ? \"document\" : \"documents\"\n }`;\n\n if (links.length === 0) {\n entries.clear();\n listEl.replaceChildren(\n el(\"div\", { className: \"folder-view-empty\" }, \"This folder is empty\")\n );\n return;\n }\n\n const seen = new Set();\n const ordered = [];\n for (const docLink of links) {\n const key = entryKey(docLink);\n seen.add(key);\n const isFolder = docLink.type === \"folder\";\n let entry = entries.get(key);\n // Rebuild only if folder/non-folder shape flipped \u2014 that's the one\n // case where the body structure differs.\n if (!entry || entry.isFolder !== isFolder) {\n entry = buildEntry(docLink, enter);\n entries.set(key, entry);\n } else {\n updateEntry(entry, docLink);\n }\n ordered.push(entry.node);\n }\n\n for (const key of [...entries.keys()]) {\n if (seen.has(key)) continue;\n entries.delete(key);\n }\n\n listEl.replaceChildren(...ordered);\n }\n\n const onChange = () => render();\n handle.on(\"change\", onChange);\n render();\n\n return () => {\n handle.off(\"change\", onChange);\n createNew.dispose();\n element.replaceChildren();\n entries.clear();\n mounted = null;\n };\n};\n", "@layer package {\n:root,\n:host,\n[theme] {\n /* The well the cards sit in \u2014 slightly recessed surface. */\n --folder-view-well: var(--editor-fill-offset-10, #f2f2f2);\n --folder-view-well-edge: var(--editor-fill-offset-30, #d6d6d6);\n --folder-view-fg: var(--editor-line, black);\n --folder-view-muted: var(--editor-line-offset-50, #999);\n --folder-view-border: var(--editor-fill-offset-20, #e5e5e5);\n\n /* A card = a raised surface sitting on the well. */\n --folder-view-card: var(--editor-fill, white);\n --folder-view-card-border: var(--editor-fill-offset-20, #e5e5e5);\n\n --folder-view-accent: var(--studio-primary-fill, var(--studio-primary, #35f7ca));\n --folder-view-accent-line: var(--studio-primary-line, var(--editor-fill, white));\n --folder-view-accent-text: var(--editor-primary-text, var(--studio-primary-text, var(--studio-primary, #35f7ca)));\n --folder-view-hover: var(--editor-fill-offset-10, #f2f2f2);\n\n /* Layered drop shadow so a card reads as a solid object resting on the\n well \u2014 a tight contact shadow plus a softer cast one. Derived from the\n theme line colour so it inverts in dark themes. */\n --folder-view-card-shadow: 0 1px 2px\n color-mix(in oklch, var(--editor-line, black), transparent 94%),\n 0 2px 8px color-mix(in oklch, var(--editor-line, black), transparent 96%);\n --folder-view-card-shadow-hover: 0 2px 4px\n color-mix(in oklch, var(--editor-line, black), transparent 90%),\n 0 8px 20px color-mix(in oklch, var(--editor-line, black), transparent 93%);\n\n --folder-view-family: var(--editor-family-sans, system-ui, sans-serif);\n}\n}\n\n/* Block: the whole tool. A sunken well with an inset edge. */\n.folder-view {\n height: 100%;\n overflow: auto;\n padding: var(--studio-space-md, 1rem);\n display: flex;\n flex-direction: column;\n gap: var(--studio-space-md, 1rem);\n font-family: var(--folder-view-family);\n color: var(--folder-view-fg);\n background: var(--folder-view-well);\n /* No outer border: the host frame already draws the lines around the pane, so\n the well reads as flush rather than double-edged. */\n}\n\n/* State: a document is being dragged in from the sidebar. Outline the well\n with the accent colour so it reads as an active drop target. */\n.folder-view[data-drop-active] {\n border-color: var(--folder-view-accent);\n box-shadow: inset 0 0 0 2px var(--folder-view-accent);\n}\n\n/* Composition: header row. New-doc button on the left, count on the right. */\n.folder-view .folder-view-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--studio-space-sm, 0.5rem);\n padding-bottom: var(--studio-space-sm, 0.5rem);\n border-bottom: 1px solid var(--folder-view-border);\n}\n\n/* Composition: where we are inside a pushwork directory doc, in place of the\n create-new control (pushwork owns those entries). */\n.folder-view .folder-view-crumbs:not([hidden]) {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--studio-space-2xs, 0.25rem);\n min-width: 0;\n font-size: 0.85rem;\n}\n\n.folder-view .folder-view-crumb {\n font: inherit;\n color: var(--folder-view-fg);\n background: none;\n border: none;\n padding: var(--studio-space-2xs, 0.25rem);\n border-radius: var(--studio-radius-sm, 4px);\n}\n\n.folder-view button.folder-view-crumb {\n color: var(--folder-view-accent-text);\n cursor: pointer;\n}\n\n.folder-view button.folder-view-crumb:hover {\n background: var(--folder-view-hover);\n}\n\n.folder-view .folder-view-crumb-sep {\n color: var(--folder-view-muted);\n}\n\n/* Composition: the create-new control \u2014 a button anchoring a type-picker. */\n.folder-view .folder-create {\n position: relative;\n}\n\n.folder-view .folder-create-button {\n display: inline-flex;\n align-items: center;\n gap: var(--studio-space-2xs, 0.25rem);\n font: inherit;\n font-size: 0.8rem;\n color: var(--folder-view-accent-line);\n background: var(--folder-view-accent);\n border: 1px solid transparent;\n border-radius: var(--studio-radius-sm, 4px);\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n cursor: pointer;\n transition: filter var(--studio-transition-fast, 0.1s ease);\n}\n\n.folder-view .folder-create-button:hover {\n filter: brightness(0.96);\n}\n\n.folder-view .folder-create-icon {\n font-size: 1rem;\n line-height: 1;\n}\n\n/* The type-picker popover, anchored under the button. */\n.folder-view .folder-create-menu {\n position: absolute;\n top: calc(100% + var(--studio-space-2xs, 0.25rem));\n left: 0;\n z-index: 10;\n min-width: 12rem;\n max-height: 16rem;\n overflow: auto;\n display: flex;\n flex-direction: column;\n padding: var(--studio-space-3xs, 0.125rem);\n background: var(--folder-view-card);\n border: 1px solid var(--folder-view-card-border);\n border-radius: var(--studio-radius-md, 8px);\n box-shadow: var(--folder-view-card-shadow-hover);\n}\n\n.folder-view .folder-create-menu[hidden] {\n display: none;\n}\n\n.folder-view .folder-create-item {\n text-align: left;\n font: inherit;\n font-size: 0.85rem;\n color: var(--folder-view-fg);\n background: transparent;\n border: 0;\n border-radius: var(--studio-radius-sm, 4px);\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n cursor: pointer;\n}\n\n.folder-view .folder-create-item:hover {\n background: var(--folder-view-hover);\n}\n\n.folder-view .folder-create-empty {\n padding: var(--studio-space-sm, 0.5rem);\n font-size: 0.8rem;\n color: var(--folder-view-muted);\n}\n\n.folder-view .folder-view-count {\n font-size: 0.8rem;\n color: var(--folder-view-muted);\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n border-radius: var(--studio-radius-round, 9999px);\n background: var(--folder-view-card);\n border: 1px solid var(--folder-view-border);\n}\n\n/* Composition: the list of cards. */\n.folder-view .folder-view-list {\n display: flex;\n flex-direction: column;\n gap: var(--studio-space-md, 1rem);\n padding-bottom: var(--studio-space-md, 1rem);\n}\n\n.folder-view .folder-view-empty {\n text-align: center;\n color: var(--folder-view-muted);\n padding: var(--studio-space-xl, 2rem) 0;\n}\n\n.folder-view-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: var(--studio-space-md, 1rem);\n font-family: var(--folder-view-family);\n color: var(--folder-view-muted);\n background: var(--folder-view-card);\n}\n\n/* Block: one rendered file/folder card \u2014 sitting on the well. */\n.folder-view .folder-entry {\n background: var(--folder-view-card);\n border: 1px solid var(--folder-view-card-border);\n border-radius: var(--studio-radius-lg, 12px);\n box-shadow: var(--folder-view-card-shadow);\n overflow: hidden;\n transition: box-shadow var(--studio-transition, 0.15s ease),\n transform var(--studio-transition, 0.15s ease);\n}\n\n.folder-view .folder-entry:hover {\n box-shadow: var(--folder-view-card-shadow-hover);\n /* Lift slightly so the card feels like it peels up off the well. */\n transform: translateY(-1px);\n}\n\n.folder-view .folder-entry-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--studio-space-sm, 0.5rem);\n padding: var(--studio-space-sm, 0.5rem) var(--studio-space-md, 1rem);\n}\n\n.folder-view .folder-entry-title {\n display: flex;\n align-items: baseline;\n gap: var(--studio-space-sm, 0.5rem);\n min-width: 0;\n}\n\n.folder-view .folder-entry-name {\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.folder-view .folder-entry-type {\n font-size: 0.7rem;\n color: var(--folder-view-muted);\n padding: var(--studio-space-3xs, 0.125rem) var(--studio-space-2xs, 0.25rem);\n border: 1px solid var(--folder-view-border);\n border-radius: var(--studio-radius-sm, 4px);\n flex-shrink: 0;\n}\n\n.folder-view .folder-entry-open {\n font: inherit;\n font-size: 0.8rem;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--folder-view-accent-text);\n text-decoration: none;\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n border-radius: var(--studio-radius-sm, 4px);\n flex-shrink: 0;\n transition: background var(--studio-transition-fast, 0.1s ease);\n}\n\n.folder-view .folder-entry-open:hover {\n background: var(--folder-view-hover);\n}\n\n.folder-view .folder-entry-hint {\n margin: 0;\n padding: 0 var(--studio-space-md, 1rem) var(--studio-space-md, 1rem);\n font-size: 0.85rem;\n color: var(--folder-view-muted);\n}\n\n/* Block: embedded preview body. */\n.folder-view .folder-entry-body {\n flex: 1 1 auto;\n min-height: 0;\n max-height: 300px;\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--folder-view-border);\n}\n\n.folder-view .folder-entry-scroll {\n height: 100%;\n overflow: auto;\n}\n\n.folder-view .folder-entry-scroll patchwork-view {\n display: block;\n height: 100%;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -500,8 +500,8 @@
|
|
|
500
500
|
display: flex;
|
|
501
501
|
justify-content: flex-end;
|
|
502
502
|
align-items: center;
|
|
503
|
-
padding-
|
|
504
|
-
border-
|
|
503
|
+
padding-bottom: 0.65rem;
|
|
504
|
+
border-bottom: 1px solid var(--pw-packages-border);
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
/* show-more toggle, valid inside the plugin/registry <ul>s */
|
package/plugin-registry-url
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
automerge:
|
|
1
|
+
automerge:2svkrnCFj1bKneLTeSEYaeXXSrjP
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// src/datatype.js
|
|
2
|
-
var FolderDatatype = {
|
|
3
|
-
init(doc) {
|
|
4
|
-
doc.title = "New Folder";
|
|
5
|
-
doc.docs = [];
|
|
6
|
-
},
|
|
7
|
-
getTitle: (doc) => doc.title || "Unnamed Folder",
|
|
8
|
-
setTitle: (doc, title) => {
|
|
9
|
-
doc.title = title;
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
var FileDatatype = {
|
|
13
|
-
init: () => {
|
|
14
|
-
throw new Error("Can't create empty File");
|
|
15
|
-
},
|
|
16
|
-
getTitle(doc) {
|
|
17
|
-
return doc.name || "Unnamed File";
|
|
18
|
-
},
|
|
19
|
-
setTitle(doc, title) {
|
|
20
|
-
doc.name = title;
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
export {
|
|
24
|
-
FileDatatype,
|
|
25
|
-
FolderDatatype
|
|
26
|
-
};
|
|
27
|
-
//# sourceMappingURL=datatype-HWGUFBTQ.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/datatype.js"],
|
|
4
|
-
"sourcesContent": ["export const FolderDatatype = {\n init(doc) {\n doc.title = 'New Folder';\n doc.docs = [];\n },\n getTitle: (doc) => doc.title || 'Unnamed Folder',\n setTitle: (doc, title) => {\n doc.title = title;\n },\n};\n\nexport const FileDatatype = {\n init: () => {\n throw new Error(\"Can't create empty File\");\n },\n getTitle(doc) {\n return doc.name || 'Unnamed File';\n },\n setTitle(doc, title) {\n doc.name = title;\n },\n};\n"],
|
|
5
|
-
"mappings": ";AAAO,IAAM,iBAAiB;AAAA,EAC5B,KAAK,KAAK;AACR,QAAI,QAAQ;AACZ,QAAI,OAAO,CAAC;AAAA,EACd;AAAA,EACA,UAAU,CAAC,QAAQ,IAAI,SAAS;AAAA,EAChC,UAAU,CAAC,KAAK,UAAU;AACxB,QAAI,QAAQ;AAAA,EACd;AACF;AAEO,IAAM,eAAe;AAAA,EAC1B,MAAM,MAAM;AACV,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAAA,EACA,SAAS,KAAK;AACZ,WAAO,IAAI,QAAQ;AAAA,EACrB;AAAA,EACA,SAAS,KAAK,OAAO;AACnB,QAAI,OAAO;AAAA,EACb;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/tool.js", "../src/styles.css"],
|
|
4
|
-
"sourcesContent": ["import {\n parseAutomergeUrl,\n isValidAutomergeUrl,\n} from \"@automerge/automerge-repo/slim\";\nimport { openDocument } from \"@inkandswitch/patchwork-elements\";\nimport {\n getRegistry,\n createDocOfDatatype2,\n} from \"@inkandswitch/patchwork-plugins\";\nimport styles from \"./styles.css\";\n\n// MIME types we can extract document drags from, in order of preference.\n// These mirror what the sideboard sets on dragstart so dropping a sidebar\n// item into the folder view adds it to the folder.\nconst DND_DATA_TYPES = [\n \"text/x-patchwork-dnd\",\n \"text/x-patchwork-urls\",\n \"text/uri-list\",\n \"text/plain\",\n];\n\nfunction hasDocumentDrag(dataTransfer) {\n return Boolean(\n dataTransfer &&\n DND_DATA_TYPES.some((type) => dataTransfer.types.includes(type))\n );\n}\n\nfunction urlFromText(text) {\n const trimmed = text.trim();\n if (isValidAutomergeUrl(trimmed)) return trimmed;\n // patchwork web links carry the document id in the fragment: #doc=<documentId>\n const docId = trimmed.match(/#doc=([^&\\s]+)/)?.[1];\n if (docId && isValidAutomergeUrl(`automerge:${docId}`)) {\n return `automerge:${docId}`;\n }\n return null;\n}\n\n// Extract the dragged documents from a drop event. Returns an array of\n// { url, name?, type? } items, or an empty array if there's nothing droppable.\nfunction getDndItems(event) {\n const data = event.dataTransfer;\n if (!data) return [];\n\n const dndData = data.getData(\"text/x-patchwork-dnd\");\n if (dndData) {\n try {\n const parsed = JSON.parse(dndData);\n if (Array.isArray(parsed?.items) && parsed.items.length > 0) {\n return parsed.items.filter((item) => isValidAutomergeUrl(item?.url));\n }\n } catch {\n // fall through to the other types\n }\n }\n\n const urlData = data.getData(\"text/x-patchwork-urls\");\n if (urlData) {\n try {\n const urls = JSON.parse(urlData);\n const items = (Array.isArray(urls) ? urls : [])\n .filter((url) => isValidAutomergeUrl(url))\n .map((url) => ({ url }));\n if (items.length > 0) return items;\n } catch {\n // fall through to the other types\n }\n }\n\n const text = data.getData(\"text/uri-list\") || data.getData(\"text/plain\");\n return text\n .split(/\\r?\\n/)\n .map(urlFromText)\n .filter((url) => url !== null)\n .map((url) => ({ url }));\n}\n\nfunction el(tag, attrs, ...children) {\n const node = document.createElement(tag);\n if (attrs) {\n for (const [key, value] of Object.entries(attrs)) {\n if (value == null || value === false) continue;\n if (key === \"className\") {\n node.className = value;\n } else if (key.startsWith(\"on\") && typeof value === \"function\") {\n node.addEventListener(key.slice(2).toLowerCase(), value);\n } else {\n node.setAttribute(key, value);\n }\n }\n }\n for (const child of children.flat()) {\n if (child == null || child === false) continue;\n node.appendChild(\n typeof child === \"string\" ? document.createTextNode(child) : child\n );\n }\n return node;\n}\n\nfunction entryHref(docLink) {\n return `#doc=${parseAutomergeUrl(docLink.url).documentId}&type=${docLink.type}`;\n}\n\nfunction renderLoading() {\n return el(\"div\", { className: \"folder-view-loading\" }, \"Loading\u2026\");\n}\n\nfunction buildEntry(docLink) {\n const isFolder = docLink.type === \"folder\";\n const nameEl = el(\"span\", { className: \"folder-entry-name\" }, docLink.name);\n const typeEl = el(\"span\", { className: \"folder-entry-type\" }, docLink.type);\n const openEl = el(\n \"a\",\n { className: \"folder-entry-open\", href: entryHref(docLink) },\n \"Open\"\n );\n\n // The nested view names the doc without heads; resolution (the drafts\n // `repo:handle-descriptor` answer) pins it to the active checkpoint when one\n // is checked out, so the entry freezes with the folder it lives in.\n const node = el(\n \"div\",\n { className: \"folder-entry\", \"data-type\": docLink.type },\n el(\n \"div\",\n { className: \"folder-entry-head\" },\n el(\"div\", { className: \"folder-entry-title\" }, nameEl, typeEl),\n openEl\n ),\n isFolder\n ? el(\n \"p\",\n { className: \"folder-entry-hint\" },\n 'Click \"Open\" to view folder contents'\n )\n : el(\n \"div\",\n { className: \"folder-entry-body\" },\n el(\n \"div\",\n { className: \"folder-entry-scroll\" },\n el(\"patchwork-view\", { \"doc-url\": docLink.url })\n )\n )\n );\n\n return {\n node,\n nameEl,\n typeEl,\n openEl,\n isFolder,\n url: docLink.url,\n };\n}\n\nfunction updateEntry(entry, docLink) {\n if (entry.nameEl.textContent !== docLink.name) {\n entry.nameEl.textContent = docLink.name;\n }\n if (entry.typeEl.textContent !== docLink.type) {\n entry.typeEl.textContent = docLink.type;\n }\n if (entry.node.getAttribute(\"data-type\") !== docLink.type) {\n entry.node.setAttribute(\"data-type\", docLink.type);\n }\n const href = entryHref(docLink);\n if (entry.openEl.getAttribute(\"href\") !== href) {\n entry.openEl.setAttribute(\"href\", href);\n }\n}\n\n// The listable document types, sorted by name. `file` and other unlisted\n// datatypes are hidden \u2014 you can't create an empty File, for instance.\nfunction listableDatatypes() {\n return getRegistry(\"patchwork:datatype\")\n .all()\n .filter((datatype) => !datatype.unlisted)\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n\n// Create a fresh document of the given datatype and return a DocLink for it.\n// Mirrors the sideboard's createNew: load the plugin on demand, create the doc,\n// then read its title.\nasync function createDocLink(repo, datatype) {\n const loaded = await getRegistry(\"patchwork:datatype\").load(datatype.id);\n if (!loaded) throw new Error(`couldn't load datatype \"${datatype.id}\"`);\n const docHandle = await createDocOfDatatype2(loaded, repo);\n return {\n url: docHandle.url,\n name: loaded.module.getTitle(docHandle.doc()),\n type: datatype.id,\n };\n}\n\n// The \"New\" button plus its type-picker popover. Creating a doc appends a\n// DocLink to the folder and opens it. Returns { node, dispose }.\nfunction buildCreateNew(handle, element) {\n const menu = el(\"div\", { className: \"folder-create-menu\", hidden: \"\" });\n const button = el(\n \"button\",\n {\n className: \"folder-create-button\",\n type: \"button\",\n \"aria-label\": \"Create new document\",\n },\n el(\"span\", { className: \"folder-create-icon\" }, \"+\"),\n \"New\"\n );\n const node = el(\"div\", { className: \"folder-create\" }, button, menu);\n\n let open = false;\n\n function setOpen(next) {\n open = next;\n if (open) {\n renderMenu();\n menu.removeAttribute(\"hidden\");\n node.setAttribute(\"data-open\", \"\");\n } else {\n menu.setAttribute(\"hidden\", \"\");\n node.removeAttribute(\"data-open\");\n }\n }\n\n function renderMenu() {\n const datatypes = listableDatatypes();\n if (datatypes.length === 0) {\n menu.replaceChildren(\n el(\n \"div\",\n { className: \"folder-create-empty\" },\n \"No document types available\"\n )\n );\n return;\n }\n menu.replaceChildren(\n ...datatypes.map((datatype) =>\n el(\n \"button\",\n {\n className: \"folder-create-item\",\n type: \"button\",\n onClick: () => pick(datatype),\n },\n datatype.name\n )\n )\n );\n }\n\n async function pick(datatype) {\n setOpen(false);\n const repo = element.repo;\n if (!repo) return;\n try {\n const link = await createDocLink(repo, datatype);\n handle.change((doc) => {\n doc.docs.push(link);\n });\n openDocument(element, link.url);\n } catch (error) {\n console.error(\"folder: couldn't create document\", error);\n }\n }\n\n button.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n setOpen(!open);\n });\n\n // Dismiss the menu on an outside click or Escape.\n const onPointerDown = (event) => {\n if (open && !node.contains(event.target)) setOpen(false);\n };\n const onKeyDown = (event) => {\n if (open && event.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(\"pointerdown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n\n const dispose = () => {\n document.removeEventListener(\"pointerdown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n\n return { node, dispose };\n}\n\nexport const FolderTool = (handle, element) => {\n const entries = new Map();\n\n const styleEl = el(\"style\");\n styleEl.textContent = styles;\n\n const countEl = el(\"span\", { className: \"folder-view-count\" });\n const listEl = el(\"div\", { className: \"folder-view-list\" });\n const createNew = buildCreateNew(handle, element);\n const shell = el(\n \"div\",\n { className: \"folder-view\" },\n el(\n \"div\",\n { className: \"folder-view-header\" },\n createNew.node,\n countEl\n ),\n listEl\n );\n\n element.append(styleEl);\n\n // --- drag-and-drop: accept documents dragged in from the sidebar ---\n\n // dragenter/dragleave fire for every descendant, so track nesting depth to\n // know when the pointer has actually left the folder view.\n let dragDepth = 0;\n\n function endDrag() {\n dragDepth = 0;\n shell.removeAttribute(\"data-drop-active\");\n }\n\n function addDroppedDocs(event) {\n const folder = handle.doc();\n if (!folder) return;\n\n const existing = new Set(folder.docs.map((docLink) => docLink.url));\n const selfUrl = handle.url;\n\n const links = [];\n for (const item of getDndItems(event)) {\n if (item.url === selfUrl) continue; // don't nest a folder inside itself\n if (existing.has(item.url)) continue; // already here\n existing.add(item.url); // de-dupe within a single drop too\n links.push({\n url: item.url,\n name: item.name || \"Untitled\",\n type: item.type || \"\",\n });\n }\n\n if (links.length === 0) return;\n\n handle.change((doc) => {\n doc.docs.push(...links);\n });\n }\n\n shell.addEventListener(\"dragenter\", (event) => {\n if (!hasDocumentDrag(event.dataTransfer)) return;\n event.preventDefault();\n dragDepth++;\n shell.setAttribute(\"data-drop-active\", \"\");\n });\n\n shell.addEventListener(\"dragover\", (event) => {\n if (!hasDocumentDrag(event.dataTransfer)) return;\n event.preventDefault();\n // \"link\": dropping here adds a new DocLink to the same automerge url \u2014 the\n // doc isn't moved or cloned. Requires the source's effectAllowed to permit\n // link (the sideboard sets \"all\").\n event.dataTransfer.dropEffect = \"link\";\n });\n\n shell.addEventListener(\"dragleave\", (event) => {\n if (!hasDocumentDrag(event.dataTransfer)) return;\n dragDepth--;\n if (dragDepth <= 0) endDrag();\n });\n\n shell.addEventListener(\"drop\", (event) => {\n if (!hasDocumentDrag(event.dataTransfer)) return;\n event.preventDefault();\n endDrag();\n addDroppedDocs(event);\n });\n\n let mounted = null;\n\n function show(node) {\n if (mounted !== node) {\n // keep the injected <style>, swap the rendered tree\n if (mounted) mounted.remove();\n element.append(node);\n mounted = node;\n }\n }\n\n function render() {\n const folder = handle.doc();\n if (!folder) {\n show(renderLoading());\n return;\n }\n\n show(shell);\n countEl.textContent = `${folder.docs.length} ${\n folder.docs.length === 1 ? \"document\" : \"documents\"\n }`;\n\n if (folder.docs.length === 0) {\n entries.clear();\n listEl.replaceChildren(\n el(\"div\", { className: \"folder-view-empty\" }, \"This folder is empty\")\n );\n return;\n }\n\n const seen = new Set();\n const ordered = [];\n for (const docLink of folder.docs) {\n seen.add(docLink.url);\n const isFolder = docLink.type === \"folder\";\n let entry = entries.get(docLink.url);\n // Rebuild only if folder/non-folder shape flipped \u2014 that's the one\n // case where the body structure differs.\n if (!entry || entry.isFolder !== isFolder) {\n entry = buildEntry(docLink);\n entries.set(docLink.url, entry);\n } else {\n updateEntry(entry, docLink);\n }\n ordered.push(entry.node);\n }\n\n for (const url of [...entries.keys()]) {\n if (seen.has(url)) continue;\n entries.delete(url);\n }\n\n listEl.replaceChildren(...ordered);\n }\n\n const onChange = () => render();\n handle.on(\"change\", onChange);\n render();\n\n return () => {\n handle.off(\"change\", onChange);\n createNew.dispose();\n element.replaceChildren();\n entries.clear();\n mounted = null;\n };\n};\n", "@layer package {\n:root,\n:host,\n[theme] {\n /* The well the cards sit in \u2014 slightly recessed surface. */\n --folder-view-well: var(--editor-fill-offset-10, #f2f2f2);\n --folder-view-well-edge: var(--editor-fill-offset-30, #d6d6d6);\n --folder-view-fg: var(--editor-line, black);\n --folder-view-muted: var(--editor-line-offset-50, #999);\n --folder-view-border: var(--editor-fill-offset-20, #e5e5e5);\n\n /* A card = a raised surface sitting on the well. */\n --folder-view-card: var(--editor-fill, white);\n --folder-view-card-border: var(--editor-fill-offset-20, #e5e5e5);\n\n --folder-view-accent: var(--studio-primary-fill, var(--studio-primary, #35f7ca));\n --folder-view-accent-line: var(--studio-primary-line, var(--editor-fill, white));\n --folder-view-accent-text: var(--editor-primary-text, var(--studio-primary-text, var(--studio-primary, #35f7ca)));\n --folder-view-hover: var(--editor-fill-offset-10, #f2f2f2);\n\n /* Layered drop shadow so a card reads as a solid object resting on the\n well \u2014 a tight contact shadow plus a softer cast one. Derived from the\n theme line colour so it inverts in dark themes. */\n --folder-view-card-shadow: 0 1px 2px\n color-mix(in oklch, var(--editor-line, black), transparent 94%),\n 0 2px 8px color-mix(in oklch, var(--editor-line, black), transparent 96%);\n --folder-view-card-shadow-hover: 0 2px 4px\n color-mix(in oklch, var(--editor-line, black), transparent 90%),\n 0 8px 20px color-mix(in oklch, var(--editor-line, black), transparent 93%);\n\n --folder-view-family: var(--editor-family-sans, system-ui, sans-serif);\n}\n}\n\n/* Block: the whole tool. A sunken well with an inset edge. */\n.folder-view {\n height: 100%;\n overflow: auto;\n padding: var(--studio-space-md, 1rem);\n display: flex;\n flex-direction: column;\n gap: var(--studio-space-md, 1rem);\n font-family: var(--folder-view-family);\n color: var(--folder-view-fg);\n background: var(--folder-view-well);\n /* No outer border: the host frame already draws the lines around the pane, so\n the well reads as flush rather than double-edged. */\n}\n\n/* State: a document is being dragged in from the sidebar. Outline the well\n with the accent colour so it reads as an active drop target. */\n.folder-view[data-drop-active] {\n border-color: var(--folder-view-accent);\n box-shadow: inset 0 0 0 2px var(--folder-view-accent);\n}\n\n/* Composition: header row. New-doc button on the left, count on the right. */\n.folder-view .folder-view-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--studio-space-sm, 0.5rem);\n padding-bottom: var(--studio-space-sm, 0.5rem);\n border-bottom: 1px solid var(--folder-view-border);\n}\n\n/* Composition: the create-new control \u2014 a button anchoring a type-picker. */\n.folder-view .folder-create {\n position: relative;\n}\n\n.folder-view .folder-create-button {\n display: inline-flex;\n align-items: center;\n gap: var(--studio-space-2xs, 0.25rem);\n font: inherit;\n font-size: 0.8rem;\n color: var(--folder-view-accent-line);\n background: var(--folder-view-accent);\n border: 1px solid transparent;\n border-radius: var(--studio-radius-sm, 4px);\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n cursor: pointer;\n transition: filter var(--studio-transition-fast, 0.1s ease);\n}\n\n.folder-view .folder-create-button:hover {\n filter: brightness(0.96);\n}\n\n.folder-view .folder-create-icon {\n font-size: 1rem;\n line-height: 1;\n}\n\n/* The type-picker popover, anchored under the button. */\n.folder-view .folder-create-menu {\n position: absolute;\n top: calc(100% + var(--studio-space-2xs, 0.25rem));\n left: 0;\n z-index: 10;\n min-width: 12rem;\n max-height: 16rem;\n overflow: auto;\n display: flex;\n flex-direction: column;\n padding: var(--studio-space-3xs, 0.125rem);\n background: var(--folder-view-card);\n border: 1px solid var(--folder-view-card-border);\n border-radius: var(--studio-radius-md, 8px);\n box-shadow: var(--folder-view-card-shadow-hover);\n}\n\n.folder-view .folder-create-menu[hidden] {\n display: none;\n}\n\n.folder-view .folder-create-item {\n text-align: left;\n font: inherit;\n font-size: 0.85rem;\n color: var(--folder-view-fg);\n background: transparent;\n border: 0;\n border-radius: var(--studio-radius-sm, 4px);\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n cursor: pointer;\n}\n\n.folder-view .folder-create-item:hover {\n background: var(--folder-view-hover);\n}\n\n.folder-view .folder-create-empty {\n padding: var(--studio-space-sm, 0.5rem);\n font-size: 0.8rem;\n color: var(--folder-view-muted);\n}\n\n.folder-view .folder-view-count {\n font-size: 0.8rem;\n color: var(--folder-view-muted);\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n border-radius: var(--studio-radius-round, 9999px);\n background: var(--folder-view-card);\n border: 1px solid var(--folder-view-border);\n}\n\n/* Composition: the list of cards. */\n.folder-view .folder-view-list {\n display: flex;\n flex-direction: column;\n gap: var(--studio-space-md, 1rem);\n padding-bottom: var(--studio-space-md, 1rem);\n}\n\n.folder-view .folder-view-empty {\n text-align: center;\n color: var(--folder-view-muted);\n padding: var(--studio-space-xl, 2rem) 0;\n}\n\n.folder-view-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: var(--studio-space-md, 1rem);\n font-family: var(--folder-view-family);\n color: var(--folder-view-muted);\n background: var(--folder-view-card);\n}\n\n/* Block: one rendered file/folder card \u2014 sitting on the well. */\n.folder-view .folder-entry {\n background: var(--folder-view-card);\n border: 1px solid var(--folder-view-card-border);\n border-radius: var(--studio-radius-lg, 12px);\n box-shadow: var(--folder-view-card-shadow);\n overflow: hidden;\n transition: box-shadow var(--studio-transition, 0.15s ease),\n transform var(--studio-transition, 0.15s ease);\n}\n\n.folder-view .folder-entry:hover {\n box-shadow: var(--folder-view-card-shadow-hover);\n /* Lift slightly so the card feels like it peels up off the well. */\n transform: translateY(-1px);\n}\n\n.folder-view .folder-entry-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--studio-space-sm, 0.5rem);\n padding: var(--studio-space-sm, 0.5rem) var(--studio-space-md, 1rem);\n}\n\n.folder-view .folder-entry-title {\n display: flex;\n align-items: baseline;\n gap: var(--studio-space-sm, 0.5rem);\n min-width: 0;\n}\n\n.folder-view .folder-entry-name {\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.folder-view .folder-entry-type {\n font-size: 0.7rem;\n color: var(--folder-view-muted);\n padding: var(--studio-space-3xs, 0.125rem) var(--studio-space-2xs, 0.25rem);\n border: 1px solid var(--folder-view-border);\n border-radius: var(--studio-radius-sm, 4px);\n flex-shrink: 0;\n}\n\n.folder-view .folder-entry-open {\n font-size: 0.8rem;\n color: var(--folder-view-accent-text);\n text-decoration: none;\n padding: var(--studio-space-2xs, 0.25rem) var(--studio-space-sm, 0.5rem);\n border-radius: var(--studio-radius-sm, 4px);\n flex-shrink: 0;\n transition: background var(--studio-transition-fast, 0.1s ease);\n}\n\n.folder-view .folder-entry-open:hover {\n background: var(--folder-view-hover);\n}\n\n.folder-view .folder-entry-hint {\n margin: 0;\n padding: 0 var(--studio-space-md, 1rem) var(--studio-space-md, 1rem);\n font-size: 0.85rem;\n color: var(--folder-view-muted);\n}\n\n/* Block: embedded preview body. */\n.folder-view .folder-entry-body {\n flex: 1 1 auto;\n min-height: 0;\n max-height: 300px;\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--folder-view-border);\n}\n\n.folder-view .folder-entry-scroll {\n height: 100%;\n overflow: auto;\n}\n\n.folder-view .folder-entry-scroll patchwork-view {\n display: block;\n height: 100%;\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|