@lapis-notes/workspace 0.3.2 → 0.3.5
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 +57 -0
- package/dist/plugin-management/PluginBrowser.svelte +8 -12
- package/dist/plugin-management/PluginRegistryContentState.svelte +6 -1
- package/dist/plugin-management/PluginRegistrySettingsPage.svelte +5 -5
- package/dist/plugin-management/PluginRegistrySkeleton.svelte +112 -0
- package/dist/plugin-management/PluginRegistrySkeleton.svelte.d.ts +7 -0
- package/dist/plugin-management/plugin-registry-controller.svelte.d.ts +2 -0
- package/dist/plugin-management/plugin-registry-controller.svelte.js +30 -13
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# @lapis-notes/workspace
|
|
2
2
|
|
|
3
|
+
## 0.3.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#85](https://github.com/lapismd/lapis-notes/pull/85)
|
|
8
|
+
[`8325b5e`](https://github.com/lapismd/lapis-notes/commit/8325b5eb7adef358ac577b457278a19637c13571)
|
|
9
|
+
Thanks [@stevejuma](https://github.com/stevejuma)! - Reuse scanned identities
|
|
10
|
+
during unchanged startup, exclude installed plugin assets from content
|
|
11
|
+
polling, avoid eagerly reading lazy ESM chunks, queue plugin operations with
|
|
12
|
+
independent progress and cancellation, and enable bundled AI by default on the
|
|
13
|
+
Rust server.
|
|
14
|
+
|
|
15
|
+
Renew the native AI feature session after disable/re-enable so startup
|
|
16
|
+
recovery cannot reuse a fenced session.
|
|
17
|
+
|
|
18
|
+
- [#82](https://github.com/lapismd/lapis-notes/pull/82)
|
|
19
|
+
[`e7b96fb`](https://github.com/lapismd/lapis-notes/commit/e7b96fb7f901c6ffff7e92815b29a82bea5c5264)
|
|
20
|
+
Thanks
|
|
21
|
+
[@lapismd-dependency-bot](https://github.com/apps/lapismd-dependency-bot)! -
|
|
22
|
+
Refresh first-party Lapis dependencies to the published versions recorded in
|
|
23
|
+
this pull request.
|
|
24
|
+
- Updated dependencies
|
|
25
|
+
[[`8325b5e`](https://github.com/lapismd/lapis-notes/commit/8325b5eb7adef358ac577b457278a19637c13571),
|
|
26
|
+
[`e7b96fb`](https://github.com/lapismd/lapis-notes/commit/e7b96fb7f901c6ffff7e92815b29a82bea5c5264)]:
|
|
27
|
+
- @lapis-notes/api@0.2.7
|
|
28
|
+
- @lapis-notes/ui@0.1.8
|
|
29
|
+
|
|
30
|
+
## 0.3.4
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- [#70](https://github.com/lapismd/lapis-notes/pull/70)
|
|
35
|
+
[`df60318`](https://github.com/lapismd/lapis-notes/commit/df603187df2680d46720ee2af0a226c6fbb25a22)
|
|
36
|
+
Thanks [@stevejuma](https://github.com/stevejuma)! - Show representative
|
|
37
|
+
skeletons while the plugin registry catalog and details load, preserving
|
|
38
|
+
existing empty/error states, verified metadata, and App-owned actions.
|
|
39
|
+
- Updated dependencies
|
|
40
|
+
[[`5bd58d8`](https://github.com/lapismd/lapis-notes/commit/5bd58d8033062923f09549fa341d84f22b143452),
|
|
41
|
+
[`8a7ec3c`](https://github.com/lapismd/lapis-notes/commit/8a7ec3c3d1d1610e8be0e3b29aab4b2f4188a0c5),
|
|
42
|
+
[`5292b52`](https://github.com/lapismd/lapis-notes/commit/5292b52ebc37318fcfad7971e4e309cf3afaff18)]:
|
|
43
|
+
- @lapis-notes/api@0.2.6
|
|
44
|
+
|
|
45
|
+
## 0.3.3
|
|
46
|
+
|
|
47
|
+
### Patch Changes
|
|
48
|
+
|
|
49
|
+
- [#66](https://github.com/lapismd/lapis-notes/pull/66)
|
|
50
|
+
[`6086980`](https://github.com/lapismd/lapis-notes/commit/6086980be8a0d9cf6e697a689b0c17f409afdc3f)
|
|
51
|
+
Thanks [@stevejuma](https://github.com/stevejuma)! - Consume published Design
|
|
52
|
+
Core 0.10.1 so browser hosts can reserve leading and trailing native controls
|
|
53
|
+
and title-bar height, with a continuous divider when sidebars collapse. Keep
|
|
54
|
+
portable registry ranges and existing compatibility.
|
|
55
|
+
- Updated dependencies
|
|
56
|
+
[[`6086980`](https://github.com/lapismd/lapis-notes/commit/6086980be8a0d9cf6e697a689b0c17f409afdc3f)]:
|
|
57
|
+
- @lapis-notes/api@0.2.5
|
|
58
|
+
- @lapis-notes/ui@0.1.7
|
|
59
|
+
|
|
3
60
|
## 0.3.2
|
|
4
61
|
|
|
5
62
|
### Patch Changes
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
import PluginBrowserLanding from "./PluginBrowserLanding.svelte";
|
|
27
27
|
import PluginBrowserDirectory from "./PluginBrowserDirectory.svelte";
|
|
28
28
|
import PluginRegistryContentState from "./PluginRegistryContentState.svelte";
|
|
29
|
+
import PluginRegistrySkeleton from "./PluginRegistrySkeleton.svelte";
|
|
29
30
|
import PluginBrowserDetail from "./PluginBrowserDetail.svelte";
|
|
30
31
|
import "./community-presentation/community-typography.css";
|
|
31
32
|
export interface PluginBrowserNavigation {
|
|
@@ -130,10 +131,7 @@
|
|
|
130
131
|
registry.actionFeedback?.pluginId === selected.id
|
|
131
132
|
? registry.actionFeedback
|
|
132
133
|
: null;
|
|
133
|
-
if (
|
|
134
|
-
registry.runningAction === `install:${selected.id}` ||
|
|
135
|
-
registry.runningAction === `update:${selected.id}`
|
|
136
|
-
)
|
|
134
|
+
if (registry.isPluginActionPending(selected.id))
|
|
137
135
|
return { state: "installing" };
|
|
138
136
|
if (feedback?.state === "error")
|
|
139
137
|
return { state: "rejected", reason: feedback.message };
|
|
@@ -409,10 +407,9 @@
|
|
|
409
407
|
label="Registry content"
|
|
410
408
|
>
|
|
411
409
|
<div class="registry-browser">
|
|
412
|
-
{#if registry.initialLoading}<
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
description="Reading your verified registry…"
|
|
410
|
+
{#if registry.initialLoading}<PluginRegistrySkeleton
|
|
411
|
+
layout={navigation.route.page === "plugin" ? "detail" : navigation.route.page}
|
|
412
|
+
label="Loading plugins"
|
|
416
413
|
/>
|
|
417
414
|
{:else if registry.lastError && !registry.catalogEntries.length}<PluginRegistryContentState
|
|
418
415
|
kind="warning"
|
|
@@ -495,10 +492,9 @@
|
|
|
495
492
|
persist();
|
|
496
493
|
}}
|
|
497
494
|
/>
|
|
498
|
-
{:else if selected && session.loading}<
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
description="Reading verified plugin metadata…"
|
|
495
|
+
{:else if selected && session.loading}<PluginRegistrySkeleton
|
|
496
|
+
layout="detail"
|
|
497
|
+
label="Loading plugin details"
|
|
502
498
|
/>
|
|
503
499
|
{:else}<PluginRegistryContentState
|
|
504
500
|
kind="empty"
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import SearchX from "@lucide/svelte/icons/search-x";
|
|
6
6
|
import Sparkles from "@lucide/svelte/icons/sparkles";
|
|
7
7
|
import * as Button from "@lapismd/design-core/shadcn/button";
|
|
8
|
+
import PluginRegistrySkeleton from "./PluginRegistrySkeleton.svelte";
|
|
8
9
|
|
|
9
10
|
let {
|
|
10
11
|
kind,
|
|
@@ -29,6 +30,9 @@
|
|
|
29
30
|
} = $props();
|
|
30
31
|
</script>
|
|
31
32
|
|
|
33
|
+
{#if kind === "loading"}
|
|
34
|
+
<PluginRegistrySkeleton label={heading} />
|
|
35
|
+
{:else}
|
|
32
36
|
<div
|
|
33
37
|
class="lapis-plugin-registry-state"
|
|
34
38
|
data-ui-component="plugin-registry-content-state"
|
|
@@ -56,7 +60,7 @@
|
|
|
56
60
|
<AlertTriangle />
|
|
57
61
|
{:else}
|
|
58
62
|
<PackageIcon />
|
|
59
|
-
|
|
63
|
+
<Sparkles />
|
|
60
64
|
{/if}
|
|
61
65
|
</div>
|
|
62
66
|
<div class="lapis-plugin-registry-state__body">
|
|
@@ -78,3 +82,4 @@
|
|
|
78
82
|
{/if}
|
|
79
83
|
</div>
|
|
80
84
|
</div>
|
|
85
|
+
{/if}
|
|
@@ -771,8 +771,9 @@
|
|
|
771
771
|
size="icon"
|
|
772
772
|
variant="outline"
|
|
773
773
|
aria-label={`Update ${installedName(record)}`}
|
|
774
|
-
disabled={registry.
|
|
775
|
-
|
|
774
|
+
disabled={registry.isPluginActionPending(
|
|
775
|
+
record.pluginId,
|
|
776
|
+
)}
|
|
776
777
|
onclick={() => void updatePlugin(update)}
|
|
777
778
|
><ArrowUpCircle /></Button.Root
|
|
778
779
|
>
|
|
@@ -922,8 +923,7 @@
|
|
|
922
923
|
><Check data-icon="inline-start" />Bundled</Button.Root
|
|
923
924
|
>
|
|
924
925
|
{:else}
|
|
925
|
-
{@const installing =
|
|
926
|
-
registry.runningAction === `install:${entry.id}`}
|
|
926
|
+
{@const installing = registry.isPluginActionPending(entry.id)}
|
|
927
927
|
<Button.Root
|
|
928
928
|
size="sm"
|
|
929
929
|
aria-label={installing
|
|
@@ -1041,7 +1041,7 @@
|
|
|
1041
1041
|
variant="outline"
|
|
1042
1042
|
aria-label={`Update ${update.name}`}
|
|
1043
1043
|
disabled={!update.canUpdate ||
|
|
1044
|
-
registry.
|
|
1044
|
+
registry.isPluginActionPending(update.id)}
|
|
1045
1045
|
onclick={() => void updatePlugin(update)}
|
|
1046
1046
|
><ArrowUpCircle /></Button.Root
|
|
1047
1047
|
>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Skeleton } from "@lapismd/design-core/shadcn/skeleton";
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
layout = "list",
|
|
6
|
+
label = "Loading plugins",
|
|
7
|
+
}: {
|
|
8
|
+
layout?: "landing" | "directory" | "detail" | "list";
|
|
9
|
+
label?: string;
|
|
10
|
+
} = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
class="registry-skeleton"
|
|
15
|
+
data-ui-component="plugin-registry-skeleton"
|
|
16
|
+
data-layout={layout}
|
|
17
|
+
aria-busy="true"
|
|
18
|
+
>
|
|
19
|
+
<span class="sr-only" role="status">{label}</span>
|
|
20
|
+
<div class="registry-skeleton__layout" aria-hidden="true">
|
|
21
|
+
<header class="registry-skeleton__header">
|
|
22
|
+
{#if layout === "detail"}
|
|
23
|
+
<Skeleton class="registry-skeleton__avatar" />
|
|
24
|
+
{/if}
|
|
25
|
+
<div class="registry-skeleton__copy">
|
|
26
|
+
<Skeleton class="registry-skeleton__title" />
|
|
27
|
+
<Skeleton class="registry-skeleton__line" />
|
|
28
|
+
<Skeleton class="registry-skeleton__short" />
|
|
29
|
+
</div>
|
|
30
|
+
{#if layout === "detail" || layout === "directory"}
|
|
31
|
+
<Skeleton class="registry-skeleton__action" />
|
|
32
|
+
{/if}
|
|
33
|
+
</header>
|
|
34
|
+
{#if layout === "detail"}
|
|
35
|
+
<Skeleton class="registry-skeleton__banner" data-ui-part="banner" />
|
|
36
|
+
<div class="registry-skeleton__detail">
|
|
37
|
+
<div class="registry-skeleton__copy" data-ui-part="overview">
|
|
38
|
+
<Skeleton class="registry-skeleton__title" />
|
|
39
|
+
{#each Array(5) as _}<Skeleton class="registry-skeleton__line" />{/each}
|
|
40
|
+
</div>
|
|
41
|
+
<aside class="registry-skeleton__copy" data-ui-part="metadata">
|
|
42
|
+
<Skeleton class="registry-skeleton__short" />
|
|
43
|
+
{#each Array(5) as _}<Skeleton class="registry-skeleton__line" />{/each}
|
|
44
|
+
</aside>
|
|
45
|
+
</div>
|
|
46
|
+
{:else}
|
|
47
|
+
<div class="registry-skeleton__cards" data-ui-part="cards">
|
|
48
|
+
{#each Array(layout === "landing" ? 3 : layout === "directory" ? 6 : 4) as _}
|
|
49
|
+
<div class="registry-skeleton__card">
|
|
50
|
+
<Skeleton class="registry-skeleton__avatar" />
|
|
51
|
+
<div class="registry-skeleton__copy">
|
|
52
|
+
<Skeleton class="registry-skeleton__short" />
|
|
53
|
+
<Skeleton class="registry-skeleton__line" />
|
|
54
|
+
<Skeleton class="registry-skeleton__line" />
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
{/each}
|
|
58
|
+
</div>
|
|
59
|
+
{/if}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<style>
|
|
64
|
+
.registry-skeleton {
|
|
65
|
+
container-type: inline-size;
|
|
66
|
+
width: 100%;
|
|
67
|
+
min-width: 0;
|
|
68
|
+
padding: 1.5rem;
|
|
69
|
+
color: var(--ui-workspace-foreground, var(--foreground));
|
|
70
|
+
--ui-skeleton-background: color-mix(in srgb, var(--ui-workspace-foreground, var(--foreground)) 9%, var(--ui-workspace-view-background, var(--background)));
|
|
71
|
+
}
|
|
72
|
+
.registry-skeleton__layout,
|
|
73
|
+
.registry-skeleton__copy,
|
|
74
|
+
.registry-skeleton__cards,
|
|
75
|
+
.registry-skeleton__detail {
|
|
76
|
+
display: grid;
|
|
77
|
+
min-width: 0;
|
|
78
|
+
gap: 1rem;
|
|
79
|
+
}
|
|
80
|
+
.registry-skeleton__layout { gap: 2rem; }
|
|
81
|
+
.registry-skeleton__header,
|
|
82
|
+
.registry-skeleton__card {
|
|
83
|
+
display: flex;
|
|
84
|
+
min-width: 0;
|
|
85
|
+
align-items: center;
|
|
86
|
+
gap: 1rem;
|
|
87
|
+
}
|
|
88
|
+
.registry-skeleton__card {
|
|
89
|
+
border: 1px solid var(--ui-workspace-border, var(--border));
|
|
90
|
+
border-radius: var(--radius);
|
|
91
|
+
padding: 1rem;
|
|
92
|
+
}
|
|
93
|
+
.registry-skeleton__copy { flex: 1; }
|
|
94
|
+
.registry-skeleton :global(.registry-skeleton__avatar) {
|
|
95
|
+
width: 3.5rem;
|
|
96
|
+
height: 3.5rem;
|
|
97
|
+
flex-shrink: 0;
|
|
98
|
+
}
|
|
99
|
+
.registry-skeleton :global(.registry-skeleton__title) { width: min(75%, 20rem); height: 2rem; }
|
|
100
|
+
.registry-skeleton :global(.registry-skeleton__line) { height: 0.875rem; width: 100%; }
|
|
101
|
+
.registry-skeleton :global(.registry-skeleton__short) { height: 0.875rem; width: 60%; }
|
|
102
|
+
.registry-skeleton :global(.registry-skeleton__action) { height: 2.25rem; width: 6rem; }
|
|
103
|
+
.registry-skeleton :global(.registry-skeleton__banner) { width: 100%; aspect-ratio: 3 / 1; }
|
|
104
|
+
@container (min-width: 44rem) {
|
|
105
|
+
[data-layout="landing"] .registry-skeleton__cards,
|
|
106
|
+
[data-layout="directory"] .registry-skeleton__cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
107
|
+
.registry-skeleton__detail { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 2rem; }
|
|
108
|
+
}
|
|
109
|
+
@media (prefers-reduced-motion: reduce) {
|
|
110
|
+
.registry-skeleton :global([data-ui-component="skeleton"]) { animation: none; }
|
|
111
|
+
}
|
|
112
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
layout?: "landing" | "directory" | "detail" | "list";
|
|
3
|
+
label?: string;
|
|
4
|
+
};
|
|
5
|
+
declare const PluginRegistrySkeleton: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
6
|
+
type PluginRegistrySkeleton = ReturnType<typeof PluginRegistrySkeleton>;
|
|
7
|
+
export default PluginRegistrySkeleton;
|
|
@@ -14,6 +14,8 @@ export declare class PluginRegistryController {
|
|
|
14
14
|
initialLoading: boolean;
|
|
15
15
|
refreshing: boolean;
|
|
16
16
|
runningAction: string | null;
|
|
17
|
+
pendingPluginActions: Record<string, true>;
|
|
18
|
+
isPluginActionPending(pluginId: string): boolean;
|
|
17
19
|
actionFeedback: PluginRegistryActionFeedback | null;
|
|
18
20
|
pluginLifecycleRevision: number;
|
|
19
21
|
private references;
|
|
@@ -25,6 +25,10 @@ export class PluginRegistryController {
|
|
|
25
25
|
initialLoading = $state(true);
|
|
26
26
|
refreshing = $state(false);
|
|
27
27
|
runningAction = $state(null);
|
|
28
|
+
pendingPluginActions = $state({});
|
|
29
|
+
isPluginActionPending(pluginId) {
|
|
30
|
+
return this.pendingPluginActions[pluginId] === true;
|
|
31
|
+
}
|
|
28
32
|
actionFeedback = $state(null);
|
|
29
33
|
pluginLifecycleRevision = $state(0);
|
|
30
34
|
references = 0;
|
|
@@ -110,20 +114,32 @@ export class PluginRegistryController {
|
|
|
110
114
|
this.refreshTask = null;
|
|
111
115
|
}
|
|
112
116
|
async runPluginAction(actionId, pluginId, title, action) {
|
|
113
|
-
if (this.
|
|
117
|
+
if (this.isPluginActionPending(pluginId))
|
|
114
118
|
return;
|
|
115
|
-
this.
|
|
116
|
-
|
|
119
|
+
this.pendingPluginActions = {
|
|
120
|
+
...this.pendingPluginActions,
|
|
121
|
+
[pluginId]: true,
|
|
122
|
+
};
|
|
117
123
|
try {
|
|
118
|
-
await withPluginInstallProgress(this.app, { pluginId, title },
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
await withPluginInstallProgress(this.app, { pluginId, title }, async (signal) => {
|
|
125
|
+
this.runningAction = actionId;
|
|
126
|
+
this.actionFeedback = { pluginId, state: "running", message: title };
|
|
127
|
+
try {
|
|
128
|
+
await action(signal);
|
|
129
|
+
await this.refresh(false);
|
|
130
|
+
this.actionFeedback = {
|
|
131
|
+
pluginId,
|
|
132
|
+
state: "success",
|
|
133
|
+
message: actionId.startsWith("update:")
|
|
134
|
+
? "Update installed"
|
|
135
|
+
: "Installed",
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
if (this.runningAction === actionId)
|
|
140
|
+
this.runningAction = null;
|
|
141
|
+
}
|
|
142
|
+
});
|
|
127
143
|
}
|
|
128
144
|
catch (error) {
|
|
129
145
|
if (isAbortError(error)) {
|
|
@@ -139,7 +155,8 @@ export class PluginRegistryController {
|
|
|
139
155
|
new Notice(`Plugin registry action failed: ${registryErrorMessage(error)}`, undefined, this.app);
|
|
140
156
|
}
|
|
141
157
|
finally {
|
|
142
|
-
|
|
158
|
+
const { [pluginId]: _finished, ...pending } = this.pendingPluginActions;
|
|
159
|
+
this.pendingPluginActions = pending;
|
|
143
160
|
}
|
|
144
161
|
}
|
|
145
162
|
install(entry, version) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lapis-notes/workspace",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Lapis Notes workspace shell backed by design-core",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@lapis-notes/api": "^0.2.
|
|
42
|
-
"@
|
|
43
|
-
"@
|
|
44
|
-
"@
|
|
41
|
+
"@lapis-notes/api": "^0.2.7",
|
|
42
|
+
"@lapis-notes/ui": "^0.1.8",
|
|
43
|
+
"@lapismd/design-core": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.12.0",
|
|
44
|
+
"@lucide/svelte": "^0.523.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"svelte": "^5.0.0"
|