@lapis-notes/workspace 0.1.0 → 0.2.1
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/CHANGELOG.md +34 -0
- package/LICENSE.md +3 -3
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/plugin-management/CommunityPluginsSettingsPage.svelte +152 -73
- package/dist/plugin-management/PluginBrowser.svelte +558 -0
- package/dist/plugin-management/PluginBrowser.svelte.d.ts +17 -0
- package/dist/plugin-management/PluginBrowserCard.svelte +230 -0
- package/dist/plugin-management/PluginBrowserCard.svelte.d.ts +9 -0
- package/dist/plugin-management/PluginBrowserDetail.svelte +1168 -0
- package/dist/plugin-management/PluginBrowserDetail.svelte.d.ts +20 -0
- package/dist/plugin-management/PluginBrowserDirectory.svelte +1069 -0
- package/dist/plugin-management/PluginBrowserDirectory.svelte.d.ts +21 -0
- package/dist/plugin-management/PluginBrowserLanding.svelte +341 -0
- package/dist/plugin-management/PluginBrowserLanding.svelte.d.ts +12 -0
- package/dist/plugin-management/PluginRegistryBadge.svelte +41 -0
- package/dist/plugin-management/PluginRegistryBadge.svelte.d.ts +8 -0
- package/dist/plugin-management/PluginRegistryContentState.svelte +80 -0
- package/dist/plugin-management/PluginRegistryContentState.svelte.d.ts +14 -0
- package/dist/plugin-management/PluginRegistryImage.svelte +64 -0
- package/dist/plugin-management/PluginRegistryImage.svelte.d.ts +9 -0
- package/dist/plugin-management/PluginRegistryMedia.svelte +223 -0
- package/dist/plugin-management/PluginRegistryMedia.svelte.d.ts +8 -0
- package/dist/plugin-management/PluginRegistryMediaViewer.svelte +155 -0
- package/dist/plugin-management/PluginRegistryMediaViewer.svelte.d.ts +13 -0
- package/dist/plugin-management/PluginRegistryRow.svelte +120 -0
- package/dist/plugin-management/PluginRegistryRow.svelte.d.ts +25 -0
- package/dist/plugin-management/PluginRegistrySettingsPage.svelte +1009 -512
- package/dist/plugin-management/PluginRegistryStatus.svelte +33 -0
- package/dist/plugin-management/PluginRegistryStatus.svelte.d.ts +8 -0
- package/dist/plugin-management/PluginRegistryToolbar.svelte +159 -0
- package/dist/plugin-management/PluginRegistryToolbar.svelte.d.ts +21 -0
- package/dist/plugin-management/community-presentation/CommunityChip.svelte +229 -0
- package/dist/plugin-management/community-presentation/CommunityChip.svelte.d.ts +4 -0
- package/dist/plugin-management/community-presentation/CommunityMemberAvatar.svelte +42 -0
- package/dist/plugin-management/community-presentation/CommunityMemberAvatar.svelte.d.ts +13 -0
- package/dist/plugin-management/community-presentation/RegistryMediaGallery.svelte +282 -0
- package/dist/plugin-management/community-presentation/RegistryMediaGallery.svelte.d.ts +8 -0
- package/dist/plugin-management/community-presentation/RegistryMediaViewer.css +179 -0
- package/dist/plugin-management/community-presentation/RegistryMediaViewer.svelte +241 -0
- package/dist/plugin-management/community-presentation/RegistryMediaViewer.svelte.d.ts +14 -0
- package/dist/plugin-management/community-presentation/RegistryPluginChangelog.svelte +216 -0
- package/dist/plugin-management/community-presentation/RegistryPluginChangelog.svelte.d.ts +8 -0
- package/dist/plugin-management/community-presentation/RegistryVerifiedImage.svelte +179 -0
- package/dist/plugin-management/community-presentation/RegistryVerifiedImage.svelte.d.ts +15 -0
- package/dist/plugin-management/community-presentation/avatar-model.d.ts +14 -0
- package/dist/plugin-management/community-presentation/avatar-model.js +21 -0
- package/dist/plugin-management/community-presentation/community-member-avatar.css +137 -0
- package/dist/plugin-management/community-presentation/community-typography.css +30 -0
- package/dist/plugin-management/community-presentation/model-adapter.d.ts +54 -0
- package/dist/plugin-management/community-presentation/model-adapter.js +90 -0
- package/dist/plugin-management/community-presentation/model.d.ts +9 -0
- package/dist/plugin-management/community-presentation/model.js +40 -0
- package/dist/plugin-management/community-presentation/props.d.ts +21 -0
- package/dist/plugin-management/community-presentation/props.js +1 -0
- package/dist/plugin-management/community-presentation/registry-filter-icons.d.ts +3 -0
- package/dist/plugin-management/community-presentation/registry-filter-icons.js +70 -0
- package/dist/plugin-management/community-presentation/registry-markdown.d.ts +1 -0
- package/dist/plugin-management/community-presentation/registry-markdown.js +57 -0
- package/dist/plugin-management/community-presentation/registry-plugin-icons.d.ts +2 -0
- package/dist/plugin-management/community-presentation/registry-plugin-icons.js +31 -0
- package/dist/plugin-management/plugin-browser-model.d.ts +44 -0
- package/dist/plugin-management/plugin-browser-model.js +104 -0
- package/dist/plugin-management/plugin-browser-navigation.d.ts +3 -0
- package/dist/plugin-management/plugin-browser-navigation.js +38 -0
- package/dist/plugin-management/plugin-download-stats.d.ts +1 -0
- package/dist/plugin-management/plugin-download-stats.js +15 -0
- package/dist/plugin-management/plugin-management.css +1088 -117
- package/dist/plugin-management/plugin-registry-controller.svelte.d.ts +29 -0
- package/dist/plugin-management/plugin-registry-controller.svelte.js +154 -0
- package/dist/plugin-management/plugin-registry-detail-session.svelte.d.ts +18 -0
- package/dist/plugin-management/plugin-registry-detail-session.svelte.js +106 -0
- package/dist/plugin-management/plugin-registry-detail.d.ts +17 -0
- package/dist/plugin-management/plugin-registry-detail.js +1 -0
- package/dist/plugin-management/plugins-view.svelte.d.ts +41 -0
- package/dist/plugin-management/plugins-view.svelte.js +109 -0
- package/package.json +5 -5
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { resolveRegistryMedia, } from "@lapis-notes/api";
|
|
2
|
+
export function imageReference(media) {
|
|
3
|
+
return { ...resolveRegistryMedia(media), alt: media.alt };
|
|
4
|
+
}
|
|
5
|
+
function mediaItem(media) {
|
|
6
|
+
return {
|
|
7
|
+
...media,
|
|
8
|
+
images: {
|
|
9
|
+
preview: resolveRegistryMedia(media, "preview"),
|
|
10
|
+
full: resolveRegistryMedia(media, "full"),
|
|
11
|
+
light: resolveRegistryMedia(media, "light"),
|
|
12
|
+
dark: resolveRegistryMedia(media, "dark"),
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function communityDetail(entry, detail) {
|
|
17
|
+
return {
|
|
18
|
+
entry,
|
|
19
|
+
detail: {
|
|
20
|
+
...detail,
|
|
21
|
+
status: detail.status ?? entry.status ?? "active",
|
|
22
|
+
appearance: detail.appearance
|
|
23
|
+
? {
|
|
24
|
+
...detail.appearance,
|
|
25
|
+
logo: detail.appearance.logo
|
|
26
|
+
? imageReference(detail.appearance.logo)
|
|
27
|
+
: undefined,
|
|
28
|
+
}
|
|
29
|
+
: undefined,
|
|
30
|
+
media: detail.media
|
|
31
|
+
? {
|
|
32
|
+
banner: detail.media.banner
|
|
33
|
+
? mediaItem(detail.media.banner)
|
|
34
|
+
: undefined,
|
|
35
|
+
overview: detail.media.overview
|
|
36
|
+
? mediaItem(detail.media.overview)
|
|
37
|
+
: undefined,
|
|
38
|
+
}
|
|
39
|
+
: undefined,
|
|
40
|
+
gallery: detail.gallery?.map((item) => ({
|
|
41
|
+
...mediaItem(item),
|
|
42
|
+
id: item.id,
|
|
43
|
+
})),
|
|
44
|
+
versions: Object.fromEntries(Object.entries(detail.versions).map(([version, release]) => [
|
|
45
|
+
version,
|
|
46
|
+
{
|
|
47
|
+
...release,
|
|
48
|
+
bundle: {
|
|
49
|
+
...release.bundle,
|
|
50
|
+
size: release.bundle.size ?? 0,
|
|
51
|
+
pending: "pending" in release.bundle && release.bundle.pending === true,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
])),
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const statusLabels = {
|
|
59
|
+
active: "Available",
|
|
60
|
+
pending: "Coming soon",
|
|
61
|
+
deprecated: "Deprecated",
|
|
62
|
+
disabled: "Unavailable",
|
|
63
|
+
revoked: "Revoked",
|
|
64
|
+
};
|
|
65
|
+
export function registryStatusLabel(status = "active") {
|
|
66
|
+
return statusLabels[status] ?? titleCaseRegistryValue(status);
|
|
67
|
+
}
|
|
68
|
+
export function titleCaseRegistryValue(value) {
|
|
69
|
+
return value
|
|
70
|
+
.split("-")
|
|
71
|
+
.map((part) => part.toLocaleLowerCase() === "ai"
|
|
72
|
+
? "AI"
|
|
73
|
+
: `${part.charAt(0).toLocaleUpperCase()}${part.slice(1)}`)
|
|
74
|
+
.join(" ");
|
|
75
|
+
}
|
|
76
|
+
export function formatRegistryBytes(size) {
|
|
77
|
+
if (!Number.isFinite(size) || size < 0)
|
|
78
|
+
return "Unknown size";
|
|
79
|
+
if (size < 1024)
|
|
80
|
+
return `${size} B`;
|
|
81
|
+
const units = ["KB", "MB", "GB"];
|
|
82
|
+
let value = size / 1024;
|
|
83
|
+
for (const unit of units) {
|
|
84
|
+
if (value < 1024 || unit === units.at(-1)) {
|
|
85
|
+
return `${value >= 10 ? value.toFixed(0) : value.toFixed(1)} ${unit}`;
|
|
86
|
+
}
|
|
87
|
+
value /= 1024;
|
|
88
|
+
}
|
|
89
|
+
return `${size} B`;
|
|
90
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const COMMUNITY_CHIP_VARIANTS: readonly ["outline", "muted", "solid", "subtle"];
|
|
2
|
+
export type CommunityChipVariant = (typeof COMMUNITY_CHIP_VARIANTS)[number];
|
|
3
|
+
export declare const COMMUNITY_CHIP_TONES: readonly ["neutral", "accent", "success", "warning", "danger"];
|
|
4
|
+
export type CommunityChipTone = (typeof COMMUNITY_CHIP_TONES)[number];
|
|
5
|
+
export declare const COMMUNITY_CHIP_SIZES: readonly ["indicator", "inline", "compact", "default"];
|
|
6
|
+
export type CommunityChipSize = (typeof COMMUNITY_CHIP_SIZES)[number];
|
|
7
|
+
export declare const COMMUNITY_CHIP_SHAPES: readonly ["pill", "square"];
|
|
8
|
+
export type CommunityChipShape = (typeof COMMUNITY_CHIP_SHAPES)[number];
|
|
9
|
+
export declare function communityChipToneForState(state: string): CommunityChipTone;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const COMMUNITY_CHIP_VARIANTS = [
|
|
2
|
+
"outline",
|
|
3
|
+
"muted",
|
|
4
|
+
"solid",
|
|
5
|
+
"subtle",
|
|
6
|
+
];
|
|
7
|
+
export const COMMUNITY_CHIP_TONES = [
|
|
8
|
+
"neutral",
|
|
9
|
+
"accent",
|
|
10
|
+
"success",
|
|
11
|
+
"warning",
|
|
12
|
+
"danger",
|
|
13
|
+
];
|
|
14
|
+
export const COMMUNITY_CHIP_SIZES = [
|
|
15
|
+
"indicator",
|
|
16
|
+
"inline",
|
|
17
|
+
"compact",
|
|
18
|
+
"default",
|
|
19
|
+
];
|
|
20
|
+
export const COMMUNITY_CHIP_SHAPES = ["pill", "square"];
|
|
21
|
+
const successfulStates = new Set(["active", "approved", "available", "signed"]);
|
|
22
|
+
const warningStates = new Set(["inspecting", "partial", "pending"]);
|
|
23
|
+
const dangerousStates = new Set([
|
|
24
|
+
"disabled",
|
|
25
|
+
"invalid",
|
|
26
|
+
"rejected",
|
|
27
|
+
"revoked",
|
|
28
|
+
"unavailable",
|
|
29
|
+
"untrusted",
|
|
30
|
+
"withdrawn",
|
|
31
|
+
]);
|
|
32
|
+
export function communityChipToneForState(state) {
|
|
33
|
+
if (successfulStates.has(state))
|
|
34
|
+
return "success";
|
|
35
|
+
if (warningStates.has(state))
|
|
36
|
+
return "warning";
|
|
37
|
+
if (dangerousStates.has(state))
|
|
38
|
+
return "danger";
|
|
39
|
+
return "neutral";
|
|
40
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements";
|
|
3
|
+
import type { CommunityChipShape, CommunityChipSize, CommunityChipTone, CommunityChipVariant } from "./model.js";
|
|
4
|
+
export type CommunityChipProps = {
|
|
5
|
+
children: Snippet;
|
|
6
|
+
leading?: Snippet;
|
|
7
|
+
variant?: CommunityChipVariant;
|
|
8
|
+
tone?: CommunityChipTone;
|
|
9
|
+
size?: CommunityChipSize;
|
|
10
|
+
shape?: CommunityChipShape;
|
|
11
|
+
href?: string;
|
|
12
|
+
onclick?: (event: MouseEvent) => void;
|
|
13
|
+
buttonProps?: HTMLButtonAttributes;
|
|
14
|
+
linkProps?: HTMLAnchorAttributes;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
pressed?: boolean;
|
|
17
|
+
ariaLabel?: string;
|
|
18
|
+
ariaHidden?: boolean;
|
|
19
|
+
title?: string;
|
|
20
|
+
class?: string;
|
|
21
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import RefreshCwIcon from "@lucide/svelte/icons/refresh-cw";
|
|
2
|
+
import BotIcon from "@lucide/svelte/icons/bot";
|
|
3
|
+
import Clock3Icon from "@lucide/svelte/icons/clock-3";
|
|
4
|
+
import CloudUploadIcon from "@lucide/svelte/icons/cloud-upload";
|
|
5
|
+
import CompassIcon from "@lucide/svelte/icons/compass";
|
|
6
|
+
import FileCode2Icon from "@lucide/svelte/icons/file-code-2";
|
|
7
|
+
import HistoryIcon from "@lucide/svelte/icons/history";
|
|
8
|
+
import SmartphoneIcon from "@lucide/svelte/icons/smartphone";
|
|
9
|
+
import BadgeCheckIcon from "@lucide/svelte/icons/badge-check";
|
|
10
|
+
import BlocksIcon from "@lucide/svelte/icons/blocks";
|
|
11
|
+
import CircleOffIcon from "@lucide/svelte/icons/circle-off";
|
|
12
|
+
import DatabaseIcon from "@lucide/svelte/icons/database";
|
|
13
|
+
import FileTextIcon from "@lucide/svelte/icons/file-text";
|
|
14
|
+
import GlobeIcon from "@lucide/svelte/icons/globe";
|
|
15
|
+
import LayoutDashboardIcon from "@lucide/svelte/icons/layout-dashboard";
|
|
16
|
+
import MonitorIcon from "@lucide/svelte/icons/monitor";
|
|
17
|
+
import NetworkIcon from "@lucide/svelte/icons/network";
|
|
18
|
+
import NotebookTabsIcon from "@lucide/svelte/icons/notebook-tabs";
|
|
19
|
+
import PackageIcon from "@lucide/svelte/icons/package";
|
|
20
|
+
import PencilIcon from "@lucide/svelte/icons/pencil";
|
|
21
|
+
import PresentationIcon from "@lucide/svelte/icons/presentation";
|
|
22
|
+
import SearchIcon from "@lucide/svelte/icons/search";
|
|
23
|
+
import ShieldCheckIcon from "@lucide/svelte/icons/shield-check";
|
|
24
|
+
import SlidersHorizontalIcon from "@lucide/svelte/icons/sliders-horizontal";
|
|
25
|
+
import SquareDashedMousePointerIcon from "@lucide/svelte/icons/square-dashed-mouse-pointer";
|
|
26
|
+
import TriangleAlertIcon from "@lucide/svelte/icons/triangle-alert";
|
|
27
|
+
import WrenchIcon from "@lucide/svelte/icons/wrench";
|
|
28
|
+
import ZapIcon from "@lucide/svelte/icons/zap";
|
|
29
|
+
export const registryFilterIcons = {
|
|
30
|
+
ai: BotIcon,
|
|
31
|
+
updates: RefreshCwIcon,
|
|
32
|
+
active: BadgeCheckIcon,
|
|
33
|
+
all: SlidersHorizontalIcon,
|
|
34
|
+
automation: ZapIcon,
|
|
35
|
+
backup: CloudUploadIcon,
|
|
36
|
+
canvas: SquareDashedMousePointerIcon,
|
|
37
|
+
data: DatabaseIcon,
|
|
38
|
+
database: DatabaseIcon,
|
|
39
|
+
deprecated: TriangleAlertIcon,
|
|
40
|
+
desktop: MonitorIcon,
|
|
41
|
+
"developer-tools": WrenchIcon,
|
|
42
|
+
diagnostics: SlidersHorizontalIcon,
|
|
43
|
+
disabled: CircleOffIcon,
|
|
44
|
+
documents: FileTextIcon,
|
|
45
|
+
editor: PencilIcon,
|
|
46
|
+
export: FileTextIcon,
|
|
47
|
+
graph: NetworkIcon,
|
|
48
|
+
history: HistoryIcon,
|
|
49
|
+
knowledge: NetworkIcon,
|
|
50
|
+
markdown: FileCode2Icon,
|
|
51
|
+
mobile: SmartphoneIcon,
|
|
52
|
+
navigation: CompassIcon,
|
|
53
|
+
notebook: NotebookTabsIcon,
|
|
54
|
+
official: ShieldCheckIcon,
|
|
55
|
+
organization: LayoutDashboardIcon,
|
|
56
|
+
package: PackageIcon,
|
|
57
|
+
pending: Clock3Icon,
|
|
58
|
+
presentation: PresentationIcon,
|
|
59
|
+
productivity: ZapIcon,
|
|
60
|
+
revoked: ShieldCheckIcon,
|
|
61
|
+
search: SearchIcon,
|
|
62
|
+
themes: BlocksIcon,
|
|
63
|
+
verified: BadgeCheckIcon,
|
|
64
|
+
viewer: FileTextIcon,
|
|
65
|
+
visualization: NetworkIcon,
|
|
66
|
+
web: GlobeIcon,
|
|
67
|
+
};
|
|
68
|
+
export function registryFilterIcon(value) {
|
|
69
|
+
return registryFilterIcons[value.toLocaleLowerCase()] ?? PackageIcon;
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function renderRegistryMarkdown(markdown: string): string;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
function escapeHtml(value) {
|
|
2
|
+
return value
|
|
3
|
+
.replaceAll("&", "&")
|
|
4
|
+
.replaceAll("<", "<")
|
|
5
|
+
.replaceAll(">", ">")
|
|
6
|
+
.replaceAll('"', """)
|
|
7
|
+
.replaceAll("'", "'");
|
|
8
|
+
}
|
|
9
|
+
function renderInline(value) {
|
|
10
|
+
return escapeHtml(value)
|
|
11
|
+
.replace(/`([^`]+)`/gu, "<code>$1</code>")
|
|
12
|
+
.replace(/\*\*([^*]+)\*\*/gu, "<strong>$1</strong>")
|
|
13
|
+
.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/gu, (_match, label, url) => `<a href="${url}" target="_blank" rel="noopener noreferrer">${label}</a>`);
|
|
14
|
+
}
|
|
15
|
+
export function renderRegistryMarkdown(markdown) {
|
|
16
|
+
const output = [];
|
|
17
|
+
const paragraphs = [];
|
|
18
|
+
const listItems = [];
|
|
19
|
+
const flushParagraph = () => {
|
|
20
|
+
if (paragraphs.length === 0)
|
|
21
|
+
return;
|
|
22
|
+
output.push(`<p>${renderInline(paragraphs.join(" "))}</p>`);
|
|
23
|
+
paragraphs.length = 0;
|
|
24
|
+
};
|
|
25
|
+
const flushList = () => {
|
|
26
|
+
if (listItems.length === 0)
|
|
27
|
+
return;
|
|
28
|
+
output.push(`<ul>${listItems.map((item) => `<li>${renderInline(item)}</li>`).join("")}</ul>`);
|
|
29
|
+
listItems.length = 0;
|
|
30
|
+
};
|
|
31
|
+
for (const line of markdown.replaceAll("\r\n", "\n").split("\n")) {
|
|
32
|
+
const heading = /^(#{1,3})\s+(.+)$/u.exec(line);
|
|
33
|
+
if (heading) {
|
|
34
|
+
flushParagraph();
|
|
35
|
+
flushList();
|
|
36
|
+
const level = heading[1].length;
|
|
37
|
+
output.push(`<h${level}>${renderInline(heading[2])}</h${level}>`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (line.startsWith("- ")) {
|
|
41
|
+
flushParagraph();
|
|
42
|
+
listItems.push(line.slice(2));
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (line.trim() === "") {
|
|
46
|
+
flushParagraph();
|
|
47
|
+
flushList();
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
flushList();
|
|
51
|
+
paragraphs.push(line.trim());
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
flushParagraph();
|
|
55
|
+
flushList();
|
|
56
|
+
return output.join("\n");
|
|
57
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import BookmarkIcon from "@lucide/svelte/icons/bookmark";
|
|
2
|
+
import FileCodeIcon from "@lucide/svelte/icons/file-code-2";
|
|
3
|
+
import FileTextIcon from "@lucide/svelte/icons/file-text";
|
|
4
|
+
import HistoryIcon from "@lucide/svelte/icons/history";
|
|
5
|
+
import ListChecksIcon from "@lucide/svelte/icons/list-checks";
|
|
6
|
+
import NetworkIcon from "@lucide/svelte/icons/network";
|
|
7
|
+
import PackageIcon from "@lucide/svelte/icons/package";
|
|
8
|
+
import PresentationIcon from "@lucide/svelte/icons/presentation";
|
|
9
|
+
import SearchIcon from "@lucide/svelte/icons/search";
|
|
10
|
+
import SparklesIcon from "@lucide/svelte/icons/sparkles";
|
|
11
|
+
import SpellCheckIcon from "@lucide/svelte/icons/spell-check-2";
|
|
12
|
+
import TableIcon from "@lucide/svelte/icons/table-2";
|
|
13
|
+
import WholeWordIcon from "@lucide/svelte/icons/whole-word";
|
|
14
|
+
const registryPluginIcons = {
|
|
15
|
+
bookmark: BookmarkIcon,
|
|
16
|
+
"file-code-2": FileCodeIcon,
|
|
17
|
+
"file-text": FileTextIcon,
|
|
18
|
+
history: HistoryIcon,
|
|
19
|
+
"list-checks": ListChecksIcon,
|
|
20
|
+
network: NetworkIcon,
|
|
21
|
+
package: PackageIcon,
|
|
22
|
+
presentation: PresentationIcon,
|
|
23
|
+
search: SearchIcon,
|
|
24
|
+
sparkles: SparklesIcon,
|
|
25
|
+
"spell-check-2": SpellCheckIcon,
|
|
26
|
+
"table-2": TableIcon,
|
|
27
|
+
"whole-word": WholeWordIcon,
|
|
28
|
+
};
|
|
29
|
+
export function registryPluginIcon(value) {
|
|
30
|
+
return registryPluginIcons[value?.toLocaleLowerCase() ?? ""] ?? PackageIcon;
|
|
31
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { PluginCatalogEntry } from "@lapis-notes/api";
|
|
2
|
+
export type PluginBrowserRoute = {
|
|
3
|
+
page: "landing" | "directory";
|
|
4
|
+
} | {
|
|
5
|
+
page: "plugin";
|
|
6
|
+
pluginId: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
};
|
|
9
|
+
export interface PluginDirectoryState {
|
|
10
|
+
query: string;
|
|
11
|
+
categories: string[];
|
|
12
|
+
platforms: string[];
|
|
13
|
+
statuses: string[];
|
|
14
|
+
officialOnly: boolean;
|
|
15
|
+
updatesOnly: boolean;
|
|
16
|
+
installation: "all" | "installed" | "available";
|
|
17
|
+
sort: string;
|
|
18
|
+
view: "grid" | "list";
|
|
19
|
+
}
|
|
20
|
+
export declare const defaultDirectoryState: () => PluginDirectoryState;
|
|
21
|
+
import { titleCaseRegistryValue as titleCase } from "./community-presentation/model-adapter";
|
|
22
|
+
export { titleCase };
|
|
23
|
+
export declare function categoriesFor(plugins: readonly PluginCatalogEntry[]): {
|
|
24
|
+
id: string;
|
|
25
|
+
count: number;
|
|
26
|
+
label: string;
|
|
27
|
+
}[];
|
|
28
|
+
/** Community's existing landing selection rules; no separate popularity data. */
|
|
29
|
+
export declare function landingSections(plugins: readonly PluginCatalogEntry[], updateIds?: ReadonlySet<string>): {
|
|
30
|
+
featured: PluginCatalogEntry[];
|
|
31
|
+
newlyReleased: PluginCatalogEntry[];
|
|
32
|
+
recentlyUpdated: PluginCatalogEntry[];
|
|
33
|
+
updates: PluginCatalogEntry[];
|
|
34
|
+
categories: {
|
|
35
|
+
id: string;
|
|
36
|
+
count: number;
|
|
37
|
+
label: string;
|
|
38
|
+
}[];
|
|
39
|
+
};
|
|
40
|
+
export declare function filterPlugins(plugins: readonly PluginCatalogEntry[], state: PluginDirectoryState, installed: Set<string>, downloads?: (id: string) => number, updateIds?: ReadonlySet<string>): PluginCatalogEntry[];
|
|
41
|
+
export declare function restoreBrowserState(value: Record<string, unknown>): {
|
|
42
|
+
route: PluginBrowserRoute;
|
|
43
|
+
directory: PluginDirectoryState;
|
|
44
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export const defaultDirectoryState = () => ({
|
|
2
|
+
query: "",
|
|
3
|
+
categories: [],
|
|
4
|
+
platforms: [],
|
|
5
|
+
statuses: [],
|
|
6
|
+
officialOnly: false,
|
|
7
|
+
updatesOnly: false,
|
|
8
|
+
installation: "all",
|
|
9
|
+
sort: "name",
|
|
10
|
+
view: "grid",
|
|
11
|
+
});
|
|
12
|
+
import { titleCaseRegistryValue as titleCase } from "./community-presentation/model-adapter";
|
|
13
|
+
export { titleCase };
|
|
14
|
+
const compareName = (a, b) => a.name.localeCompare(b.name) || a.id.localeCompare(b.id);
|
|
15
|
+
const compareDate = (a, b) => (b.latestRelease?.releasedAt ?? "").localeCompare(a.latestRelease?.releasedAt ?? "") || compareName(a, b);
|
|
16
|
+
export function categoriesFor(plugins) {
|
|
17
|
+
const counts = new Map();
|
|
18
|
+
for (const plugin of plugins)
|
|
19
|
+
for (const category of plugin.categories)
|
|
20
|
+
counts.set(category, (counts.get(category) ?? 0) + 1);
|
|
21
|
+
return [...counts]
|
|
22
|
+
.map(([id, count]) => ({ id, count, label: titleCase(id) }))
|
|
23
|
+
.sort((a, b) => a.label.localeCompare(b.label) || a.id.localeCompare(b.id));
|
|
24
|
+
}
|
|
25
|
+
/** Community's existing landing selection rules; no separate popularity data. */
|
|
26
|
+
export function landingSections(plugins, updateIds = new Set()) {
|
|
27
|
+
return {
|
|
28
|
+
featured: plugins
|
|
29
|
+
.filter((p) => (p.status ?? "active") === "active")
|
|
30
|
+
.sort(compareName)
|
|
31
|
+
.slice(0, 5),
|
|
32
|
+
newlyReleased: [...plugins].sort(compareDate).slice(0, 5),
|
|
33
|
+
recentlyUpdated: [...plugins].sort(compareDate).slice(0, 5),
|
|
34
|
+
updates: plugins
|
|
35
|
+
.filter((plugin) => updateIds.has(plugin.id))
|
|
36
|
+
.sort(compareName),
|
|
37
|
+
categories: categoriesFor(plugins).slice(0, 8),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function filterPlugins(plugins, state, installed, downloads = () => 0, updateIds = new Set()) {
|
|
41
|
+
const text = state.query.trim().toLowerCase();
|
|
42
|
+
return plugins
|
|
43
|
+
.filter((p) => (!text ||
|
|
44
|
+
`${p.name} ${p.id} ${p.description} ${p.author}`
|
|
45
|
+
.toLowerCase()
|
|
46
|
+
.includes(text)) &&
|
|
47
|
+
(!state.categories.length ||
|
|
48
|
+
p.categories.some((c) => state.categories.includes(c))) &&
|
|
49
|
+
(!state.platforms.length ||
|
|
50
|
+
p.platforms.some((v) => state.platforms.includes(v))) &&
|
|
51
|
+
(!state.statuses.length ||
|
|
52
|
+
state.statuses.includes(p.status ?? "active")) &&
|
|
53
|
+
(!state.officialOnly || p.channel === "official") &&
|
|
54
|
+
(!state.updatesOnly || updateIds.has(p.id)) &&
|
|
55
|
+
(state.installation === "all" ||
|
|
56
|
+
installed.has(p.id) === (state.installation === "installed")))
|
|
57
|
+
.sort(state.sort === "downloads"
|
|
58
|
+
? (a, b) => downloads(b.id) - downloads(a.id) || compareName(a, b)
|
|
59
|
+
: ["new", "updated"].includes(state.sort)
|
|
60
|
+
? compareDate
|
|
61
|
+
: state.sort === "status"
|
|
62
|
+
? (a, b) => (a.status ?? "active").localeCompare(b.status ?? "active") ||
|
|
63
|
+
compareName(a, b)
|
|
64
|
+
: compareName);
|
|
65
|
+
}
|
|
66
|
+
export function restoreBrowserState(value) {
|
|
67
|
+
const routeValue = value.route;
|
|
68
|
+
const route = routeValue?.page === "plugin" &&
|
|
69
|
+
typeof routeValue.pluginId === "string" &&
|
|
70
|
+
routeValue.pluginId.length > 0 &&
|
|
71
|
+
(routeValue.version === undefined ||
|
|
72
|
+
(typeof routeValue.version === "string" &&
|
|
73
|
+
/^[A-Za-z0-9][A-Za-z0-9.+-]{0,127}$/u.test(routeValue.version)))
|
|
74
|
+
? {
|
|
75
|
+
page: "plugin",
|
|
76
|
+
pluginId: routeValue.pluginId,
|
|
77
|
+
...(routeValue.version !== undefined
|
|
78
|
+
? { version: routeValue.version }
|
|
79
|
+
: {}),
|
|
80
|
+
}
|
|
81
|
+
: { page: routeValue?.page === "directory" ? "directory" : "landing" };
|
|
82
|
+
const directory = defaultDirectoryState();
|
|
83
|
+
const input = value.directory;
|
|
84
|
+
if (input && typeof input === "object") {
|
|
85
|
+
if (typeof input.query === "string")
|
|
86
|
+
directory.query = input.query;
|
|
87
|
+
for (const key of ["categories", "platforms", "statuses"])
|
|
88
|
+
if (Array.isArray(input[key]) &&
|
|
89
|
+
input[key].every((v) => typeof v === "string"))
|
|
90
|
+
directory[key] = [...input[key]];
|
|
91
|
+
if (typeof input.officialOnly === "boolean")
|
|
92
|
+
directory.officialOnly = input.officialOnly;
|
|
93
|
+
if (typeof input.updatesOnly === "boolean")
|
|
94
|
+
directory.updatesOnly = input.updatesOnly;
|
|
95
|
+
if (["all", "installed", "available"].includes(String(input.installation)))
|
|
96
|
+
directory.installation =
|
|
97
|
+
input.installation;
|
|
98
|
+
if (["name", "new", "updated", "status", "downloads"].includes(String(input.sort)))
|
|
99
|
+
directory.sort = String(input.sort);
|
|
100
|
+
if (input.view === "list")
|
|
101
|
+
directory.view = "list";
|
|
102
|
+
}
|
|
103
|
+
return { route, directory };
|
|
104
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { PluginsView } from "./plugins-view.svelte";
|
|
2
|
+
const pending = new WeakMap();
|
|
3
|
+
/** Reveal the registered singleton browser, preserving its workspace placement. */
|
|
4
|
+
export function openPluginBrowser(app, pluginId, isActive = () => true, version) {
|
|
5
|
+
const previous = pending.get(app) ?? Promise.resolve();
|
|
6
|
+
const next = previous
|
|
7
|
+
.catch(() => { })
|
|
8
|
+
.then(async () => {
|
|
9
|
+
if (!isActive())
|
|
10
|
+
return;
|
|
11
|
+
const leaf = app.workspace.getLeavesOfType("plugins")[0] ??
|
|
12
|
+
app.workspace.getLeaf(true);
|
|
13
|
+
if (leaf.view.getViewType() !== "plugins")
|
|
14
|
+
await leaf.setViewState({
|
|
15
|
+
type: "plugins",
|
|
16
|
+
state: { route: { page: "landing" } },
|
|
17
|
+
});
|
|
18
|
+
if (!isActive())
|
|
19
|
+
return;
|
|
20
|
+
if (!(leaf.view instanceof PluginsView))
|
|
21
|
+
throw new Error("The Plugins view is not registered in this workspace.");
|
|
22
|
+
if (pluginId && version !== undefined)
|
|
23
|
+
leaf.view.showPlugin(pluginId, version);
|
|
24
|
+
else if (pluginId)
|
|
25
|
+
leaf.view.showPlugin(pluginId);
|
|
26
|
+
else
|
|
27
|
+
leaf.view.home();
|
|
28
|
+
await app.workspace.revealLeaf(leaf);
|
|
29
|
+
if (isActive())
|
|
30
|
+
app.workspace.activateLeaf(leaf);
|
|
31
|
+
})
|
|
32
|
+
.finally(() => {
|
|
33
|
+
if (pending.get(app) === next)
|
|
34
|
+
pending.delete(app);
|
|
35
|
+
});
|
|
36
|
+
pending.set(app, next);
|
|
37
|
+
return next;
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatApproximateDownloadCount(value: number): string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function formatApproximateDownloadCount(value) {
|
|
2
|
+
if (value < 1_000)
|
|
3
|
+
return String(value);
|
|
4
|
+
for (const [threshold, suffix] of [
|
|
5
|
+
[1_000_000_000, "B"],
|
|
6
|
+
[1_000_000, "M"],
|
|
7
|
+
[1_000, "K"],
|
|
8
|
+
]) {
|
|
9
|
+
if (value >= threshold) {
|
|
10
|
+
const scaled = value / threshold;
|
|
11
|
+
return `${scaled >= 10 ? Math.round(scaled) : scaled.toFixed(1).replace(/\.0$/, "")}${suffix}`;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return String(value);
|
|
15
|
+
}
|