@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,14 @@
|
|
|
1
|
+
import type { RegistryMediaItem } from "./model-adapter";
|
|
2
|
+
import "./RegistryMediaViewer.css";
|
|
3
|
+
type MediaMode = "preview" | "full" | "light" | "dark";
|
|
4
|
+
type $$ComponentProps = {
|
|
5
|
+
items: readonly RegistryMediaItem[];
|
|
6
|
+
pluginName: string;
|
|
7
|
+
open?: boolean;
|
|
8
|
+
selectedIndex?: number;
|
|
9
|
+
initialMode?: MediaMode;
|
|
10
|
+
opener?: HTMLElement | null;
|
|
11
|
+
};
|
|
12
|
+
declare const RegistryMediaViewer: import("svelte").Component<$$ComponentProps, {}, "open" | "selectedIndex">;
|
|
13
|
+
type RegistryMediaViewer = ReturnType<typeof RegistryMediaViewer>;
|
|
14
|
+
export default RegistryMediaViewer;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
type RegistryChangelogTag,
|
|
4
|
+
type RegistryRelease,
|
|
5
|
+
} from "./model-adapter";
|
|
6
|
+
import CommunityChip from "./CommunityChip.svelte";
|
|
7
|
+
|
|
8
|
+
import { renderRegistryMarkdown } from "./registry-markdown";
|
|
9
|
+
let {
|
|
10
|
+
latestVersion,
|
|
11
|
+
releases,
|
|
12
|
+
}: {
|
|
13
|
+
latestVersion: string;
|
|
14
|
+
releases: readonly RegistryRelease[];
|
|
15
|
+
} = $props();
|
|
16
|
+
|
|
17
|
+
const dateFormatter = new Intl.DateTimeFormat("en", {
|
|
18
|
+
dateStyle: "medium",
|
|
19
|
+
timeZone: "UTC",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const tagLabels: Record<RegistryChangelogTag, string> = {
|
|
23
|
+
feature: "Feature",
|
|
24
|
+
improvement: "Improvement",
|
|
25
|
+
fix: "Fix",
|
|
26
|
+
security: "Security",
|
|
27
|
+
breaking: "Breaking",
|
|
28
|
+
deprecation: "Deprecation",
|
|
29
|
+
removal: "Removal",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function versionLabel(version: string): string {
|
|
33
|
+
return version.startsWith("v") ? version : `v${version}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function releasedAt(release: RegistryRelease): string {
|
|
37
|
+
return dateFormatter.format(new Date(release.releasedAt));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function noteTitle(release: RegistryRelease): string {
|
|
41
|
+
return (
|
|
42
|
+
release.changelog?.title ?? `${versionLabel(release.version)} release`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function noteDescription(release: RegistryRelease): string | undefined {
|
|
47
|
+
return release.changelog?.description ?? release.releasePost;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function noteHtml(release: RegistryRelease): string | undefined {
|
|
51
|
+
const description = noteDescription(release);
|
|
52
|
+
return description === undefined
|
|
53
|
+
? undefined
|
|
54
|
+
: renderRegistryMarkdown(description);
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<div class="registry-plugin-changelog-list">
|
|
59
|
+
{#each releases as release (release.version)}
|
|
60
|
+
<article
|
|
61
|
+
class="registry-plugin-changelog"
|
|
62
|
+
aria-label={`${release.version} changelog`}
|
|
63
|
+
>
|
|
64
|
+
<header class="registry-plugin-changelog__header">
|
|
65
|
+
<div class="registry-plugin-changelog__chips">
|
|
66
|
+
<CommunityChip
|
|
67
|
+
class="registry-plugin-changelog__version"
|
|
68
|
+
variant="muted"
|
|
69
|
+
tone="success">{versionLabel(release.version)}</CommunityChip
|
|
70
|
+
>
|
|
71
|
+
{#if release.version === latestVersion}
|
|
72
|
+
<CommunityChip
|
|
73
|
+
class="registry-plugin-changelog__latest"
|
|
74
|
+
variant="outline"
|
|
75
|
+
tone="success">Latest</CommunityChip
|
|
76
|
+
>
|
|
77
|
+
{/if}
|
|
78
|
+
</div>
|
|
79
|
+
<time datetime={release.releasedAt}>{releasedAt(release)}</time>
|
|
80
|
+
</header>
|
|
81
|
+
|
|
82
|
+
<div class="registry-plugin-changelog__prose">
|
|
83
|
+
<h2 class="registry-plugin-changelog__title">{noteTitle(release)}</h2>
|
|
84
|
+
{#if release.changelog?.tags?.length}
|
|
85
|
+
<div
|
|
86
|
+
class="registry-plugin-changelog__tags"
|
|
87
|
+
aria-label="Changelog tags"
|
|
88
|
+
>
|
|
89
|
+
{#each release.changelog.tags as tag}
|
|
90
|
+
<CommunityChip variant="subtle" size="compact"
|
|
91
|
+
>{tagLabels[tag]}</CommunityChip
|
|
92
|
+
>
|
|
93
|
+
{/each}
|
|
94
|
+
</div>
|
|
95
|
+
{/if}
|
|
96
|
+
{#if noteHtml(release)}
|
|
97
|
+
{@html noteHtml(release)}
|
|
98
|
+
{:else}
|
|
99
|
+
<p>This release does not include verified changelog notes.</p>
|
|
100
|
+
{/if}
|
|
101
|
+
</div>
|
|
102
|
+
</article>
|
|
103
|
+
{:else}
|
|
104
|
+
<article class="registry-plugin-changelog" aria-label="No changelog">
|
|
105
|
+
<div class="registry-plugin-changelog__prose">
|
|
106
|
+
<h2 class="registry-plugin-changelog__title">
|
|
107
|
+
No structured changelog yet
|
|
108
|
+
</h2>
|
|
109
|
+
<p>This plugin does not include verified changelog notes.</p>
|
|
110
|
+
</div>
|
|
111
|
+
</article>
|
|
112
|
+
{/each}
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<style>
|
|
116
|
+
.registry-plugin-changelog-list {
|
|
117
|
+
display: grid;
|
|
118
|
+
gap: 1rem;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.registry-plugin-changelog {
|
|
122
|
+
display: grid;
|
|
123
|
+
min-width: 0;
|
|
124
|
+
gap: 1.4rem;
|
|
125
|
+
padding: clamp(1.4rem, 4vw, 2.4rem);
|
|
126
|
+
border: 1px solid color-mix(in srgb, var(--border) 86%, transparent);
|
|
127
|
+
border-radius: calc(var(--radius) * 1.8);
|
|
128
|
+
background: var(--background);
|
|
129
|
+
box-shadow: 0 1.25rem 3rem
|
|
130
|
+
color-mix(in srgb, var(--foreground) 7%, transparent);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.registry-plugin-changelog__header {
|
|
134
|
+
display: flex;
|
|
135
|
+
min-width: 0;
|
|
136
|
+
align-items: center;
|
|
137
|
+
justify-content: space-between;
|
|
138
|
+
gap: 1rem;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.registry-plugin-changelog__chips {
|
|
142
|
+
display: flex;
|
|
143
|
+
min-width: 0;
|
|
144
|
+
align-items: center;
|
|
145
|
+
flex-wrap: wrap;
|
|
146
|
+
gap: 0.7rem;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.registry-plugin-changelog__header time {
|
|
150
|
+
color: color-mix(in srgb, var(--muted-foreground) 84%, var(--foreground));
|
|
151
|
+
font-size: var(--community-text-section);
|
|
152
|
+
font-weight: 500;
|
|
153
|
+
white-space: nowrap;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.registry-plugin-changelog__prose {
|
|
157
|
+
display: grid;
|
|
158
|
+
gap: 1rem;
|
|
159
|
+
color: color-mix(in srgb, var(--muted-foreground) 84%, var(--foreground));
|
|
160
|
+
font-size: var(--community-text-body);
|
|
161
|
+
line-height: 1.55;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.registry-plugin-changelog__prose :global(*) {
|
|
165
|
+
margin: 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.registry-plugin-changelog__title,
|
|
169
|
+
.registry-plugin-changelog__prose :global(h1) {
|
|
170
|
+
color: var(--foreground);
|
|
171
|
+
font-size: var(--community-text-page);
|
|
172
|
+
font-weight: 800;
|
|
173
|
+
line-height: 1.12;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.registry-plugin-changelog__prose :global(h2),
|
|
177
|
+
.registry-plugin-changelog__prose :global(h3) {
|
|
178
|
+
margin-top: 0.75rem;
|
|
179
|
+
color: var(--foreground);
|
|
180
|
+
font-size: var(--community-text-section);
|
|
181
|
+
font-weight: 760;
|
|
182
|
+
line-height: 1.2;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.registry-plugin-changelog__prose :global(ul),
|
|
186
|
+
.registry-plugin-changelog__prose :global(ol) {
|
|
187
|
+
display: grid;
|
|
188
|
+
gap: 0.45rem;
|
|
189
|
+
padding-inline-start: 1.65rem;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.registry-plugin-changelog__prose :global(li::marker) {
|
|
193
|
+
color: color-mix(in srgb, var(--muted-foreground) 76%, transparent);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.registry-plugin-changelog__prose :global(code) {
|
|
197
|
+
border-radius: calc(var(--radius) * 0.6);
|
|
198
|
+
background: color-mix(in srgb, var(--muted) 76%, transparent);
|
|
199
|
+
color: color-mix(in srgb, var(--foreground) 72%, var(--muted-foreground));
|
|
200
|
+
padding: 0.08em 0.28em;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.registry-plugin-changelog__tags {
|
|
204
|
+
display: flex;
|
|
205
|
+
align-items: center;
|
|
206
|
+
flex-wrap: wrap;
|
|
207
|
+
gap: 0.4rem;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@media (max-width: 34rem) {
|
|
211
|
+
.registry-plugin-changelog__header {
|
|
212
|
+
align-items: flex-start;
|
|
213
|
+
flex-direction: column;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
</style>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type RegistryRelease } from "./model-adapter";
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
latestVersion: string;
|
|
4
|
+
releases: readonly RegistryRelease[];
|
|
5
|
+
};
|
|
6
|
+
declare const RegistryPluginChangelog: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
|
+
type RegistryPluginChangelog = ReturnType<typeof RegistryPluginChangelog>;
|
|
8
|
+
export default RegistryPluginChangelog;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Skeleton } from "@lapismd/design-core/shadcn/skeleton";
|
|
3
|
+
import * as Empty from "@lapismd/design-core/shadcn/empty";
|
|
4
|
+
import ImageOff from "@lucide/svelte/icons/image-off";
|
|
5
|
+
import type { Snippet } from "svelte";
|
|
6
|
+
import type { HTMLImgAttributes } from "svelte/elements";
|
|
7
|
+
|
|
8
|
+
import { fetchVerifiedPluginImage } from "@lapis-notes/api";
|
|
9
|
+
import type { RegistryImageReference } from "./model-adapter";
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
reference,
|
|
13
|
+
alt,
|
|
14
|
+
fallback,
|
|
15
|
+
width,
|
|
16
|
+
height,
|
|
17
|
+
loading,
|
|
18
|
+
class: className,
|
|
19
|
+
}: {
|
|
20
|
+
reference: RegistryImageReference;
|
|
21
|
+
alt: string;
|
|
22
|
+
fallback?: Snippet;
|
|
23
|
+
width?: HTMLImgAttributes["width"];
|
|
24
|
+
height?: HTMLImgAttributes["height"];
|
|
25
|
+
loading?: HTMLImgAttributes["loading"];
|
|
26
|
+
class?: HTMLImgAttributes["class"];
|
|
27
|
+
} = $props();
|
|
28
|
+
|
|
29
|
+
const loader = (
|
|
30
|
+
reference: RegistryImageReference,
|
|
31
|
+
options: { signal: AbortSignal },
|
|
32
|
+
) => fetchVerifiedPluginImage(reference, fetch, options.signal);
|
|
33
|
+
let resolvedUrl = $state("");
|
|
34
|
+
let failed = $state(false);
|
|
35
|
+
let decoded = $state(false);
|
|
36
|
+
let observedElement = $state<HTMLElement>();
|
|
37
|
+
let inView = $state(false);
|
|
38
|
+
|
|
39
|
+
$effect(() => {
|
|
40
|
+
if (loader === undefined || loading !== "lazy") {
|
|
41
|
+
inView = true;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const element = observedElement;
|
|
45
|
+
if (element === undefined || typeof IntersectionObserver === "undefined") {
|
|
46
|
+
inView = true;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const observer = new IntersectionObserver(
|
|
50
|
+
(entries) => {
|
|
51
|
+
if (entries.some((entry) => entry.isIntersecting)) inView = true;
|
|
52
|
+
},
|
|
53
|
+
{ rootMargin: "240px" },
|
|
54
|
+
);
|
|
55
|
+
observer.observe(element);
|
|
56
|
+
return () => observer.disconnect();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
$effect(() => {
|
|
60
|
+
const current = reference;
|
|
61
|
+
if (loader === undefined) {
|
|
62
|
+
resolvedUrl = current.url;
|
|
63
|
+
failed = false;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!inView) return;
|
|
67
|
+
|
|
68
|
+
const controller = new AbortController();
|
|
69
|
+
let active = true;
|
|
70
|
+
let objectUrl = "";
|
|
71
|
+
resolvedUrl = "";
|
|
72
|
+
failed = false;
|
|
73
|
+
decoded = false;
|
|
74
|
+
void loader(current, { signal: controller.signal })
|
|
75
|
+
.then((blob) => {
|
|
76
|
+
if (!active) return;
|
|
77
|
+
objectUrl = URL.createObjectURL(blob);
|
|
78
|
+
resolvedUrl = objectUrl;
|
|
79
|
+
})
|
|
80
|
+
.catch((cause: unknown) => {
|
|
81
|
+
if (
|
|
82
|
+
active &&
|
|
83
|
+
(!(cause instanceof DOMException) || cause.name !== "AbortError")
|
|
84
|
+
) {
|
|
85
|
+
failed = true;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return () => {
|
|
90
|
+
active = false;
|
|
91
|
+
controller.abort();
|
|
92
|
+
if (objectUrl !== "") URL.revokeObjectURL(objectUrl);
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
<div
|
|
98
|
+
bind:this={observedElement}
|
|
99
|
+
class={["registry-verified-image", className].filter(Boolean).join(" ")}
|
|
100
|
+
data-image-state={failed ? "failed" : decoded ? "loaded" : "loading"}
|
|
101
|
+
style:aspect-ratio={width && height ? `${width} / ${height}` : undefined}
|
|
102
|
+
role={!decoded && alt ? "img" : undefined}
|
|
103
|
+
aria-label={!decoded && alt
|
|
104
|
+
? `${alt}${failed ? " unavailable" : " loading"}`
|
|
105
|
+
: undefined}
|
|
106
|
+
aria-busy={!decoded && !failed ? "true" : undefined}
|
|
107
|
+
>
|
|
108
|
+
{#if resolvedUrl && !failed}
|
|
109
|
+
<img
|
|
110
|
+
src={resolvedUrl}
|
|
111
|
+
{alt}
|
|
112
|
+
{width}
|
|
113
|
+
{height}
|
|
114
|
+
loading="eager"
|
|
115
|
+
class:decoded
|
|
116
|
+
onload={() => (decoded = true)}
|
|
117
|
+
onerror={() => {
|
|
118
|
+
failed = true;
|
|
119
|
+
decoded = false;
|
|
120
|
+
}}
|
|
121
|
+
/>
|
|
122
|
+
{/if}
|
|
123
|
+
{#if failed}
|
|
124
|
+
{#if fallback}{@render fallback()}{:else}
|
|
125
|
+
<Empty.Root
|
|
126
|
+
class="registry-verified-image__empty"
|
|
127
|
+
aria-hidden={alt === "" ? "true" : undefined}
|
|
128
|
+
>
|
|
129
|
+
<Empty.Header>
|
|
130
|
+
<Empty.Media variant="icon"
|
|
131
|
+
><ImageOff aria-hidden="true" /></Empty.Media
|
|
132
|
+
>
|
|
133
|
+
{#if alt}<Empty.Title>Image unavailable</Empty.Title>{/if}
|
|
134
|
+
</Empty.Header>
|
|
135
|
+
</Empty.Root>
|
|
136
|
+
{/if}
|
|
137
|
+
{:else if !decoded}
|
|
138
|
+
<Skeleton class="registry-verified-image__skeleton" aria-hidden="true" />
|
|
139
|
+
{/if}
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<style>
|
|
143
|
+
.registry-verified-image {
|
|
144
|
+
display: grid;
|
|
145
|
+
place-items: center;
|
|
146
|
+
width: 100%;
|
|
147
|
+
height: 100%;
|
|
148
|
+
min-width: 0;
|
|
149
|
+
min-height: 2rem;
|
|
150
|
+
overflow: hidden;
|
|
151
|
+
border-radius: inherit;
|
|
152
|
+
}
|
|
153
|
+
.registry-verified-image > img {
|
|
154
|
+
grid-area: 1 / 1;
|
|
155
|
+
display: block;
|
|
156
|
+
width: 100%;
|
|
157
|
+
height: 100%;
|
|
158
|
+
max-width: 100%;
|
|
159
|
+
max-height: 100%;
|
|
160
|
+
object-fit: contain;
|
|
161
|
+
opacity: 0;
|
|
162
|
+
}
|
|
163
|
+
.registry-verified-image > img.decoded {
|
|
164
|
+
opacity: 1;
|
|
165
|
+
}
|
|
166
|
+
.registry-verified-image :global(.registry-verified-image__skeleton),
|
|
167
|
+
.registry-verified-image :global(.registry-verified-image__empty) {
|
|
168
|
+
grid-area: 1 / 1;
|
|
169
|
+
width: 100%;
|
|
170
|
+
height: 100%;
|
|
171
|
+
min-width: 0;
|
|
172
|
+
min-height: 0;
|
|
173
|
+
}
|
|
174
|
+
.registry-verified-image :global(.registry-verified-image__empty) {
|
|
175
|
+
padding: 0.5rem;
|
|
176
|
+
background: var(--muted);
|
|
177
|
+
color: var(--muted-foreground);
|
|
178
|
+
}
|
|
179
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { HTMLImgAttributes } from "svelte/elements";
|
|
3
|
+
import type { RegistryImageReference } from "./model-adapter";
|
|
4
|
+
type $$ComponentProps = {
|
|
5
|
+
reference: RegistryImageReference;
|
|
6
|
+
alt: string;
|
|
7
|
+
fallback?: Snippet;
|
|
8
|
+
width?: HTMLImgAttributes["width"];
|
|
9
|
+
height?: HTMLImgAttributes["height"];
|
|
10
|
+
loading?: HTMLImgAttributes["loading"];
|
|
11
|
+
class?: HTMLImgAttributes["class"];
|
|
12
|
+
};
|
|
13
|
+
declare const RegistryVerifiedImage: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
14
|
+
type RegistryVerifiedImage = ReturnType<typeof RegistryVerifiedImage>;
|
|
15
|
+
export default RegistryVerifiedImage;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type CommunityMemberModel = {
|
|
2
|
+
kind?: string;
|
|
3
|
+
};
|
|
4
|
+
export type CommunityMemberAvatarShape = "circle" | "rounded";
|
|
5
|
+
export declare function communityMemberAvatarShape(member: Pick<CommunityMemberModel, "kind">): CommunityMemberAvatarShape;
|
|
6
|
+
export declare const communityMemberAvatarFallbackTones: readonly ["blue", "emerald", "amber", "rose", "cyan", "violet", "orange"];
|
|
7
|
+
export type CommunityMemberAvatarFallbackTone = (typeof communityMemberAvatarFallbackTones)[number];
|
|
8
|
+
/**
|
|
9
|
+
* Select the same deterministic fallback tone as Buzz's shared UserAvatar.
|
|
10
|
+
* Keep the display name as the seed so renamed profiles receive the same
|
|
11
|
+
* presentation in both clients.
|
|
12
|
+
*/
|
|
13
|
+
export declare function communityMemberAvatarFallbackTone(displayName: string): CommunityMemberAvatarFallbackTone;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function communityMemberAvatarShape(member) {
|
|
2
|
+
return member.kind === "agent" ? "rounded" : "circle";
|
|
3
|
+
}
|
|
4
|
+
export const communityMemberAvatarFallbackTones = [
|
|
5
|
+
"blue",
|
|
6
|
+
"emerald",
|
|
7
|
+
"amber",
|
|
8
|
+
"rose",
|
|
9
|
+
"cyan",
|
|
10
|
+
"violet",
|
|
11
|
+
"orange",
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Select the same deterministic fallback tone as Buzz's shared UserAvatar.
|
|
15
|
+
* Keep the display name as the seed so renamed profiles receive the same
|
|
16
|
+
* presentation in both clients.
|
|
17
|
+
*/
|
|
18
|
+
export function communityMemberAvatarFallbackTone(displayName) {
|
|
19
|
+
const hash = Array.from(displayName.trim().toLowerCase()).reduce((value, character) => (value * 31 + (character.codePointAt(0) ?? 0)) >>> 0, 0);
|
|
20
|
+
return communityMemberAvatarFallbackTones[hash % communityMemberAvatarFallbackTones.length];
|
|
21
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
.community-member-avatar {
|
|
2
|
+
--community-member-avatar-size: 2.25rem;
|
|
3
|
+
--community-member-avatar-presence-size: 0.625rem;
|
|
4
|
+
|
|
5
|
+
position: relative;
|
|
6
|
+
display: block;
|
|
7
|
+
width: var(--community-member-avatar-size);
|
|
8
|
+
height: var(--community-member-avatar-size);
|
|
9
|
+
flex: none;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.community-member-avatar[data-size="inline"] {
|
|
13
|
+
--community-member-avatar-size: 1.25em;
|
|
14
|
+
|
|
15
|
+
display: inline-grid;
|
|
16
|
+
align-self: center;
|
|
17
|
+
vertical-align: middle;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.community-member-avatar[data-size="inline"] .community-member-avatar__image {
|
|
21
|
+
font-size: var(--community-text-avatar-initials);
|
|
22
|
+
line-height: 1;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.community-member-avatar[data-size="small"] {
|
|
26
|
+
--community-member-avatar-size: 2rem;
|
|
27
|
+
--community-member-avatar-presence-size: 0.55rem;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.community-member-avatar[data-size="xsmall"] {
|
|
31
|
+
--community-member-avatar-size: 1.5rem;
|
|
32
|
+
--community-member-avatar-presence-size: 0.45rem;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.community-member-avatar[data-size="xsmall"] .community-member-avatar__image {
|
|
36
|
+
font-size: var(--community-text-avatar-initials);
|
|
37
|
+
line-height: 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.community-member-avatar[data-size="large"] {
|
|
41
|
+
--community-member-avatar-size: 4rem;
|
|
42
|
+
--community-member-avatar-presence-size: 0.85rem;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.community-member-avatar[data-size="xlarge"] {
|
|
46
|
+
--community-member-avatar-size: 6rem;
|
|
47
|
+
--community-member-avatar-presence-size: 1rem;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.community-member-avatar[data-shape="rounded"] .community-member-avatar__image {
|
|
51
|
+
border-radius: 25%;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.community-member-avatar__image {
|
|
55
|
+
display: grid;
|
|
56
|
+
width: 100%;
|
|
57
|
+
height: 100%;
|
|
58
|
+
place-items: center;
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
border-radius: 50%;
|
|
61
|
+
font-size: var(--community-text-body);
|
|
62
|
+
font-weight: 700;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.community-member-avatar__image[data-fallback-tone="blue"] {
|
|
66
|
+
color: #ffffff;
|
|
67
|
+
background: #2563eb;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.community-member-avatar__image[data-fallback-tone="emerald"] {
|
|
71
|
+
color: #ffffff;
|
|
72
|
+
background: #047857;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.community-member-avatar__image[data-fallback-tone="amber"] {
|
|
76
|
+
color: #451a03;
|
|
77
|
+
background: #fbbf24;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.community-member-avatar__image[data-fallback-tone="rose"] {
|
|
81
|
+
color: #ffffff;
|
|
82
|
+
background: #e11d48;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.community-member-avatar__image[data-fallback-tone="cyan"] {
|
|
86
|
+
color: #083344;
|
|
87
|
+
background: #22d3ee;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.community-member-avatar__image[data-fallback-tone="violet"] {
|
|
91
|
+
color: #ffffff;
|
|
92
|
+
background: #7c3aed;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.community-member-avatar__image[data-fallback-tone="orange"] {
|
|
96
|
+
color: #ffffff;
|
|
97
|
+
background: #c2410c;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.community-member-avatar__image img {
|
|
101
|
+
display: block;
|
|
102
|
+
width: 100%;
|
|
103
|
+
height: 100%;
|
|
104
|
+
border-radius: inherit;
|
|
105
|
+
object-fit: cover;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.community-member-avatar__image [data-ui-part="emoji"] {
|
|
109
|
+
font-size: inherit;
|
|
110
|
+
font-weight: 400;
|
|
111
|
+
line-height: 1;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.community-member-avatar__presence {
|
|
115
|
+
position: absolute;
|
|
116
|
+
right: -0.08rem;
|
|
117
|
+
bottom: -0.08rem;
|
|
118
|
+
width: var(--community-member-avatar-presence-size);
|
|
119
|
+
height: var(--community-member-avatar-presence-size);
|
|
120
|
+
border: 2px solid
|
|
121
|
+
var(--community-member-avatar-presence-ring, var(--background));
|
|
122
|
+
border-radius: 50%;
|
|
123
|
+
background: var(--muted-foreground);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.community-member-avatar__presence[data-presence="online"] {
|
|
127
|
+
background: var(--success, #08b94e);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.community-member-avatar__presence[data-presence="away"] {
|
|
131
|
+
background: var(--warning, #d99a00);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.community-member-avatar__presence[data-presence="unknown"] {
|
|
135
|
+
background: var(--background);
|
|
136
|
+
box-shadow: inset 0 0 0 1px var(--muted-foreground);
|
|
137
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* One Community scale. Public components and portalled recipes use the same tokens. */
|
|
2
|
+
:root {
|
|
3
|
+
--community-text-body: 0.875rem;
|
|
4
|
+
--community-text-secondary: 0.75rem;
|
|
5
|
+
--community-text-avatar-initials: 0.625rem;
|
|
6
|
+
--community-text-section: 1rem;
|
|
7
|
+
--community-text-dialog: 1.125rem;
|
|
8
|
+
--community-text-page: 1.5rem;
|
|
9
|
+
--ui-dialog-frame-font-size: var(--community-text-body);
|
|
10
|
+
--ui-dialog-frame-title-size: var(--community-text-dialog);
|
|
11
|
+
}
|
|
12
|
+
:where(
|
|
13
|
+
.community-ui,
|
|
14
|
+
.community-application,
|
|
15
|
+
.community-application-root,
|
|
16
|
+
.agents,
|
|
17
|
+
.agent-profile,
|
|
18
|
+
.agent-activity
|
|
19
|
+
) {
|
|
20
|
+
font-family: var(--font-sans, sans-serif);
|
|
21
|
+
font-size: var(--community-text-body);
|
|
22
|
+
line-height: 1.5;
|
|
23
|
+
}
|
|
24
|
+
:where(.community-ui) h1 {
|
|
25
|
+
font-size: var(--community-text-page);
|
|
26
|
+
}
|
|
27
|
+
:where(.community-ui) h2,
|
|
28
|
+
:where(.community-ui) h3 {
|
|
29
|
+
font-size: var(--community-text-section);
|
|
30
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { resolveRegistryMedia, type PluginCatalogEntry, type PluginCatalogDetail, type PluginCatalogRelease, type RegistryMediaV2 } from "@lapis-notes/api";
|
|
2
|
+
export type RegistryCatalogEntry = PluginCatalogEntry;
|
|
3
|
+
export type RegistryImageReference = ReturnType<typeof resolveRegistryMedia>;
|
|
4
|
+
export type RegistryMediaItem = Omit<RegistryMediaV2, "images"> & {
|
|
5
|
+
images: Record<"preview" | "full" | "light" | "dark", RegistryImageReference>;
|
|
6
|
+
};
|
|
7
|
+
export type RegistryGalleryItem = RegistryMediaItem & {
|
|
8
|
+
id: string;
|
|
9
|
+
};
|
|
10
|
+
export type RegistryRelease = PluginCatalogRelease & {
|
|
11
|
+
releaseSequence?: string;
|
|
12
|
+
bundle: PluginCatalogRelease["bundle"] & {
|
|
13
|
+
size: number;
|
|
14
|
+
pending: boolean;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export type RegistryChangelogTag = NonNullable<NonNullable<PluginCatalogRelease["changelog"]>["tags"]>[number];
|
|
18
|
+
export type RegistryResolvedDetail = {
|
|
19
|
+
entry: PluginCatalogEntry;
|
|
20
|
+
detail: Omit<PluginCatalogDetail, "appearance" | "media" | "gallery" | "versions" | "status"> & {
|
|
21
|
+
status: NonNullable<PluginCatalogDetail["status"]>;
|
|
22
|
+
appearance?: {
|
|
23
|
+
icon: string;
|
|
24
|
+
accent: string;
|
|
25
|
+
logo?: RegistryImageReference & {
|
|
26
|
+
alt: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
media?: {
|
|
30
|
+
banner?: RegistryMediaItem;
|
|
31
|
+
overview?: RegistryMediaItem;
|
|
32
|
+
};
|
|
33
|
+
gallery?: RegistryGalleryItem[];
|
|
34
|
+
versions: Record<string, RegistryRelease>;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export type RegistryInstallAction = {
|
|
38
|
+
state: "available" | "installed" | "installing" | "disabled" | "rejected";
|
|
39
|
+
label?: string;
|
|
40
|
+
reason?: string;
|
|
41
|
+
installedVersion?: string;
|
|
42
|
+
};
|
|
43
|
+
export type RegistryInstallRequest = {
|
|
44
|
+
pluginId: string;
|
|
45
|
+
version: string;
|
|
46
|
+
bundle: RegistryRelease["bundle"];
|
|
47
|
+
};
|
|
48
|
+
export declare function imageReference(media: RegistryMediaV2): RegistryImageReference & {
|
|
49
|
+
alt: string;
|
|
50
|
+
};
|
|
51
|
+
export declare function communityDetail(entry: PluginCatalogEntry, detail: PluginCatalogDetail): RegistryResolvedDetail;
|
|
52
|
+
export declare function registryStatusLabel(status?: string): string;
|
|
53
|
+
export declare function titleCaseRegistryValue(value: string): string;
|
|
54
|
+
export declare function formatRegistryBytes(size: number): string;
|