@fungi.computer/tourtois 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/AppPane.js CHANGED
@@ -13,6 +13,33 @@ import { useWindowMotion } from "./use-window-motion.js";
13
13
  import { backHint } from "./shell-commands.js";
14
14
  import { createAppWindowManager, } from "./app-windows.js";
15
15
  import { launchComputerId } from "./app-launch.js";
16
+ /**
17
+ * The App's own path, commands and state, when it publishes them; an App
18
+ * that publishes none keeps the title and subtitle in its header.
19
+ */
20
+ function headerContent(navigation, popover, activate) {
21
+ if (navigation?.crumbs === undefined || navigation.crumbs.length === 0)
22
+ return {};
23
+ let content = {
24
+ crumbs: navigation.crumbs,
25
+ popover,
26
+ onCrumb: (crumbId, siblingId) => {
27
+ activate(siblingId === undefined
28
+ ? { kind: "crumb", crumbId }
29
+ : { kind: "crumb", crumbId, siblingId });
30
+ },
31
+ onAction: (actionId) => {
32
+ activate({ kind: "action", actionId });
33
+ },
34
+ };
35
+ if (navigation.primary !== undefined)
36
+ content = { ...content, primary: navigation.primary };
37
+ if (navigation.menu !== undefined)
38
+ content = { ...content, menu: navigation.menu };
39
+ if (navigation.status !== undefined)
40
+ content = { ...content, status: navigation.status };
41
+ return content;
42
+ }
16
43
  const windowStatusLabel = (projection) => {
17
44
  if (projection.status === "error")
18
45
  return `${projection.launch.title} unavailable`;
@@ -28,6 +55,7 @@ export function FocusedWindowControls({ app, onOpenSettings, onOpenPermissions,
28
55
  }
29
56
  export function AppPane({ projection, icon, manager, onError, index, focusWindow, afterHide, onBack, onSelectComputer, }) {
30
57
  const containerRef = useRef(null);
58
+ const popover = useLayer("popover");
31
59
  const [computerPickerOpen, setComputerPickerOpen] = useState(false);
32
60
  useEffect(() => {
33
61
  if (projection.launch.computers === undefined)
@@ -101,7 +129,14 @@ export function AppPane({ projection, icon, manager, onError, index, focusWindow
101
129
  onBack(projection.windowId);
102
130
  }, style: binding.stackingStyle, children: [_jsx(WindowHeader, { title: projection.launch.title, ...(projection.navigation?.title === undefined
103
131
  ? {}
104
- : { subtitle: projection.navigation.title }), density: layout.mode === "tiled" ? "compact" : "regular", icon: _jsx(AppIcon, { roles: projection.launch.roles, icon: icon }), ...(projection.navigation?.canGoBack === true
132
+ : { subtitle: projection.navigation.title }), ...headerContent(projection.navigation, popover, (activation) => {
133
+ void manager
134
+ .activateNavigation(projection.windowId, activation)
135
+ .catch((cause) => {
136
+ console.error("App workbench operation failed", cause);
137
+ onError("App could not handle that action.");
138
+ });
139
+ }), density: layout.mode === "tiled" ? "compact" : "regular", icon: _jsx(AppIcon, { roles: projection.launch.roles, icon: icon }), ...(projection.navigation?.canGoBack === true
105
140
  ? {
106
141
  back: {
107
142
  label: `Back in ${projection.launch.title}`,
@@ -18,19 +18,19 @@ function AvatarOverview({ onNavigate, }) {
18
18
  export function SettingsHome({ team, summary, sections, desktopOnly = false, onNavigate, }) {
19
19
  const offers = (category) => sections.includes(category);
20
20
  const close = team?.close;
21
- return (_jsxs("section", { className: "tourtois-settings-content", "aria-label": "Settings home", children: [_jsxs(DataList, { density: "compact", "aria-label": "Settings overview", children: [offers("channels") ? (_jsx(DataRow, { className: "p-0", children: _jsx(ActionRow, { "aria-label": "Website assistant", label: "Website assistant", description: "Set up chat on your WordPress site.", icon: _jsx(Sigil, { name: "connected" }), onClick: () => {
21
+ return (_jsxs("section", { className: "tourtois-settings-content", "aria-label": "Settings home", children: [_jsxs(DataList, { density: "compact", "aria-label": "Settings overview", children: [offers("channels") ? (_jsx(DataRow, { className: "p-0", children: _jsx(ActionRow, { "aria-label": "Website assistant", label: "Website assistant", description: "Set up chat on your WordPress site.", icon: _jsx(Sigil, { name: "internet" }), onClick: () => {
22
22
  onNavigate({ category: "channels" });
23
- } }) })) : null, offers("selling") ? (_jsx(DataRow, { className: "p-0", children: _jsx(ActionRow, { "aria-label": "Selling", label: "Selling", description: "Sell Apps on the Bazaar.", icon: _jsx(Sigil, { name: "installApps" }), onClick: () => {
23
+ } }) })) : null, offers("selling") ? (_jsx(DataRow, { className: "p-0", children: _jsx(ActionRow, { "aria-label": "Selling", label: "Selling", description: "Sell Apps on the Bazaar.", icon: _jsx(Sigil, { name: "sell" }), onClick: () => {
24
24
  onNavigate({ category: "selling", appId: null });
25
25
  } }) })) : null, offers("avatar") ? _jsx(AvatarOverview, { onNavigate: onNavigate }) : null, offers("defaults") ? (_jsx(DataRow, { className: "p-0", children: _jsx(ActionRow, { "aria-label": "Default apps", icon: _jsx(Sigil, { name: "installApps" }), label: "Default apps", description: "Apps for everyday tasks.", trailing: summary.defaultAppCount === null ? null : (_jsx(OverviewValue, { children: String(summary.defaultAppCount) +
26
26
  (summary.defaultAppCount === 1 ? " role" : " roles") })), onClick: () => {
27
27
  onNavigate({ category: "defaults" });
28
- } }) })) : null, team !== undefined ? (_jsx(DataRow, { className: "p-0", children: _jsx(ActionRow, { "aria-label": "Members and invites", icon: _jsx(Sigil, { name: "listAgents" }), label: "Members and invites", description: "People and invitations.", onClick: team.openMembers }) })) : null, offers("connectors") ? (_jsx(DataRow, { className: "p-0", children: _jsx(ActionRow, { "aria-label": "Connectors", icon: _jsx(Sigil, { name: "read" }), label: "Connectors", description: "What Agents can reach.", onClick: () => {
28
+ } }) })) : null, team !== undefined ? (_jsx(DataRow, { className: "p-0", children: _jsx(ActionRow, { "aria-label": "Members and invites", icon: _jsx(Sigil, { name: "members" }), label: "Members and invites", description: "People and invitations.", onClick: team.openMembers }) })) : null, offers("connectors") ? (_jsx(DataRow, { className: "p-0", children: _jsx(ActionRow, { "aria-label": "Connectors", icon: _jsx(Sigil, { name: "read" }), label: "Connectors", description: "What Agents can reach.", onClick: () => {
29
29
  onNavigate({
30
30
  category: "connectors",
31
31
  target: { kind: "list" },
32
32
  });
33
- } }) })) : null, offers("computers") ? (_jsx(DataRow, { className: "p-0", children: _jsx(ActionRow, { "aria-label": "Internet access", icon: _jsx(Sigil, { name: "connected" }), label: "Internet access", description: "Each Computer\u2019s outside access.", trailing: summary.computerCount === null ? null : (_jsx(OverviewValue, { children: String(summary.computerCount) +
33
+ } }) })) : null, offers("computers") ? (_jsx(DataRow, { className: "p-0", children: _jsx(ActionRow, { "aria-label": "Internet access", icon: _jsx(Sigil, { name: "internet" }), label: "Internet access", description: "Each Computer\u2019s outside access.", trailing: summary.computerCount === null ? null : (_jsx(OverviewValue, { children: String(summary.computerCount) +
34
34
  (summary.computerCount === 1
35
35
  ? " Computer"
36
36
  : " Computers") })), onClick: () => {
@@ -1,7 +1,7 @@
1
1
  import * as z from "zod";
2
2
  import { type AppAddress } from "@fungi.computer/oyster/shell/app-directory";
3
3
  export declare const appPermissionChangesSchema: z.ZodObject<{
4
- computer: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
4
+ computer: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodLazy<z.ZodObject<{
5
5
  kind: z.ZodLiteral<"selected-computers">;
6
6
  computers: z.ZodArray<z.ZodObject<{
7
7
  computerId: z.ZodString;
@@ -12,7 +12,7 @@ export declare const appPermissionChangesSchema: z.ZodObject<{
12
12
  "computer.execute": "computer.execute";
13
13
  }>>;
14
14
  }, z.core.$strict>>;
15
- }, z.core.$strict>, z.ZodObject<{
15
+ }, z.core.$strict>>, z.ZodLazy<z.ZodObject<{
16
16
  kind: z.ZodLiteral<"all-team-computers">;
17
17
  operations: z.ZodArray<z.ZodEnum<{
18
18
  "computer.list": "computer.list";
@@ -20,7 +20,7 @@ export declare const appPermissionChangesSchema: z.ZodObject<{
20
20
  "computer.writeFile": "computer.writeFile";
21
21
  "computer.execute": "computer.execute";
22
22
  }>>;
23
- }, z.core.$strict>], "kind">>;
23
+ }, z.core.$strict>>], "kind">>;
24
24
  agents: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
25
25
  kind: z.ZodLiteral<"all-team-agents">;
26
26
  directoryOperations: z.ZodArray<z.ZodEnum<{
@@ -58,7 +58,7 @@ export declare const appPermissionChangesSchema: z.ZodObject<{
58
58
  export type AppPermissionChanges = z.output<typeof appPermissionChangesSchema>;
59
59
  export declare const appPermissionReceiptSchema: z.ZodObject<{
60
60
  authorityVersion: z.ZodNumber;
61
- computerPermission: z.ZodNullable<z.ZodDiscriminatedUnion<[z.ZodObject<{
61
+ computerPermission: z.ZodNullable<z.ZodDiscriminatedUnion<[z.ZodLazy<z.ZodObject<{
62
62
  kind: z.ZodLiteral<"selected-computers">;
63
63
  computers: z.ZodArray<z.ZodObject<{
64
64
  computerId: z.ZodString;
@@ -69,7 +69,7 @@ export declare const appPermissionReceiptSchema: z.ZodObject<{
69
69
  "computer.execute": "computer.execute";
70
70
  }>>;
71
71
  }, z.core.$strict>>;
72
- }, z.core.$strict>, z.ZodObject<{
72
+ }, z.core.$strict>>, z.ZodLazy<z.ZodObject<{
73
73
  kind: z.ZodLiteral<"all-team-computers">;
74
74
  operations: z.ZodArray<z.ZodEnum<{
75
75
  "computer.list": "computer.list";
@@ -77,7 +77,7 @@ export declare const appPermissionReceiptSchema: z.ZodObject<{
77
77
  "computer.writeFile": "computer.writeFile";
78
78
  "computer.execute": "computer.execute";
79
79
  }>>;
80
- }, z.core.$strict>], "kind">>;
80
+ }, z.core.$strict>>], "kind">>;
81
81
  agentPermission: z.ZodDiscriminatedUnion<[z.ZodObject<{
82
82
  kind: z.ZodLiteral<"all-team-agents">;
83
83
  directoryOperations: z.ZodArray<z.ZodEnum<{
@@ -129,15 +129,15 @@ export type AppMutationRequest = ReturnType<typeof appPermissionsMutationRequest
129
129
  export declare const appMutationResponseSchema: z.ZodObject<{
130
130
  kind: z.ZodLiteral<"accepted">;
131
131
  receipt: z.ZodUnknown;
132
- enforcement: z.ZodNullable<z.ZodObject<{
132
+ enforcement: z.ZodNullable<z.ZodLazy<z.ZodObject<{
133
133
  requestId: z.ZodString;
134
- address: z.ZodUnion<readonly [z.ZodObject<{
134
+ address: z.ZodLazy<z.ZodUnion<readonly [z.ZodObject<{
135
135
  kind: z.ZodLiteral<"default">;
136
136
  appId: z.ZodString;
137
137
  }, z.core.$strict>, z.ZodObject<{
138
138
  kind: z.ZodLiteral<"installation">;
139
139
  installationId: z.ZodString;
140
- }, z.core.$strict>]>;
140
+ }, z.core.$strict>]>>;
141
141
  authorityVersion: z.ZodNumber;
142
142
  state: z.ZodEnum<{
143
143
  unaffected: "unaffected";
@@ -146,5 +146,5 @@ export declare const appMutationResponseSchema: z.ZodObject<{
146
146
  expired: "expired";
147
147
  }>;
148
148
  outstanding: z.ZodNumber;
149
- }, z.core.$strict>>;
149
+ }, z.core.$strict>>>;
150
150
  }, z.core.$strict>;
@@ -99,6 +99,33 @@ export declare const hasVisibleAppWindows: (projection: Readonly<{
99
99
  navigation: Readonly<{
100
100
  canGoBack: boolean;
101
101
  title?: string;
102
+ crumbs?: readonly Readonly<{
103
+ id: string;
104
+ label: string;
105
+ siblings?: readonly Readonly<{
106
+ id: string;
107
+ label: string;
108
+ }>[];
109
+ }>[];
110
+ primary?: readonly Readonly<{
111
+ id: string;
112
+ label: string;
113
+ variant?: "primary" | "outline" | "ghost";
114
+ tone?: "destructive";
115
+ disabled?: boolean;
116
+ }>[];
117
+ menu?: readonly Readonly<{
118
+ id: string;
119
+ label: string;
120
+ variant?: "primary" | "outline" | "ghost";
121
+ tone?: "destructive";
122
+ disabled?: boolean;
123
+ }>[];
124
+ status?: readonly Readonly<{
125
+ id: string;
126
+ label: string;
127
+ tone?: "neutral" | "info" | "warning" | "success" | "error";
128
+ }>[];
102
129
  }> | null;
103
130
  statusItems: readonly import("@fungi.computer/agar").AppStatusItem[];
104
131
  }>[];
@@ -35,7 +35,7 @@ export function LauncherGill({ items, size = "M", onAllApps, loading = false, un
35
35
  {
36
36
  id: "all-apps",
37
37
  label: "All Apps ›",
38
- icon: _jsx(Sigil, { name: "installApps" }),
38
+ icon: _jsx(Sigil, { name: "allApps" }),
39
39
  onSelect: onAllApps,
40
40
  },
41
41
  ], size: "lg" }), editing ? (_jsx(DataList, { "aria-label": "Launcher preferences", children: items.map((item) => (_jsx(DataRow, { children: _jsxs("div", { className: "tourtois-launcher-preference", children: [_jsx("span", { "aria-hidden": "true", className: "grid size-6 place-items-center [&_svg]:size-full", children: item.icon ?? _jsx(Sigil, { name: "installApps" }) }), _jsx("div", { className: "tourtois-launcher-preference-identity", children: _jsx("span", { children: item.label }) }), _jsxs(Select, { "aria-label": `Launcher state for ${item.label}`, value: stateFor(item.id), onChange: (event) => {
@@ -1,4 +1,4 @@
1
- import { APP_NAVIGATION_TITLE_MAX, parseAppStatusItems, } from "@fungi.computer/agar";
1
+ import { APP_NAVIGATION_TITLE_MAX, parseAppStatusItems, APP_NAVIGATION_CRUMBS_MAX, APP_NAVIGATION_LABEL_MAX, APP_NAVIGATION_MENU_MAX, APP_NAVIGATION_PRIMARY_MAX, APP_NAVIGATION_SIBLINGS_MAX, APP_NAVIGATION_STATUS_MAX, } from "@fungi.computer/agar";
2
2
  import { teamComputerDirectorySchema, teamComputerSchema, } from "@fungi.computer/oyster/shell/workbench";
3
3
  import { createAppSurfaceCommandHost } from "@fungi.computer/oyster/shell/command-host";
4
4
  import * as Schema from "effect/Schema";
@@ -23,10 +23,44 @@ const platformDestinationsSchema = z
23
23
  label: z.string().min(1).max(80),
24
24
  }))
25
25
  .max(8);
26
+ const relayId = z.string().min(1).max(64);
27
+ const relayLabel = z.string().min(1).max(APP_NAVIGATION_LABEL_MAX);
28
+ const relayCommands = (maximum) => z
29
+ .array(z.strictObject({
30
+ id: relayId,
31
+ label: relayLabel,
32
+ variant: z.enum(["primary", "outline", "ghost"]).optional(),
33
+ tone: z.literal("destructive").optional(),
34
+ disabled: z.boolean().optional(),
35
+ }))
36
+ .max(maximum);
26
37
  const navigationRelaySchema = z
27
38
  .strictObject({
28
39
  canGoBack: z.boolean(),
29
40
  title: z.string().trim().min(1).max(APP_NAVIGATION_TITLE_MAX).optional(),
41
+ crumbs: z
42
+ .array(z.strictObject({
43
+ id: relayId,
44
+ label: relayLabel,
45
+ siblings: z
46
+ .array(z.strictObject({ id: relayId, label: relayLabel }))
47
+ .max(APP_NAVIGATION_SIBLINGS_MAX)
48
+ .optional(),
49
+ }))
50
+ .max(APP_NAVIGATION_CRUMBS_MAX)
51
+ .optional(),
52
+ primary: relayCommands(APP_NAVIGATION_PRIMARY_MAX).optional(),
53
+ menu: relayCommands(APP_NAVIGATION_MENU_MAX).optional(),
54
+ status: z
55
+ .array(z.strictObject({
56
+ id: relayId,
57
+ label: relayLabel,
58
+ tone: z
59
+ .enum(["neutral", "info", "warning", "success", "error"])
60
+ .optional(),
61
+ }))
62
+ .max(APP_NAVIGATION_STATUS_MAX)
63
+ .optional(),
30
64
  })
31
65
  .nullable();
32
66
  /** A child's navigation state as relayed by the host, re-checked here. */
@@ -34,9 +68,38 @@ function parseNavigationRelay(value) {
34
68
  const state = navigationRelaySchema.parse(value);
35
69
  if (state === null)
36
70
  return null;
37
- return state.title === undefined
38
- ? { canGoBack: state.canGoBack }
39
- : { canGoBack: state.canGoBack, title: state.title };
71
+ let parsed = { canGoBack: state.canGoBack };
72
+ if (state.title !== undefined)
73
+ parsed = { ...parsed, title: state.title };
74
+ if (state.crumbs !== undefined)
75
+ parsed = {
76
+ ...parsed,
77
+ crumbs: state.crumbs.map((crumb) => {
78
+ const { siblings, ...rest } = crumb;
79
+ return siblings === undefined ? rest : { ...rest, siblings };
80
+ }),
81
+ };
82
+ if (state.primary !== undefined)
83
+ parsed = { ...parsed, primary: state.primary.map(commandOf) };
84
+ if (state.menu !== undefined)
85
+ parsed = { ...parsed, menu: state.menu.map(commandOf) };
86
+ if (state.status !== undefined)
87
+ parsed = { ...parsed, status: state.status.map(statusOf) };
88
+ return parsed;
89
+ }
90
+ function commandOf(command) {
91
+ let copy = { id: command.id, label: command.label };
92
+ if (command.variant !== undefined)
93
+ copy = { ...copy, variant: command.variant };
94
+ if (command.tone !== undefined)
95
+ copy = { ...copy, tone: command.tone };
96
+ if (command.disabled !== undefined)
97
+ copy = { ...copy, disabled: command.disabled };
98
+ return copy;
99
+ }
100
+ function statusOf(item) {
101
+ const copy = { id: item.id, label: item.label };
102
+ return item.tone === undefined ? copy : { ...copy, tone: item.tone };
40
103
  }
41
104
  function childLaunch(launch) {
42
105
  return {
@@ -143,6 +206,7 @@ export function createTourtoisShell(workbench) {
143
206
  reconfigure: (nextLaunch) => handle.reconfigure(childLaunch(nextLaunch)),
144
207
  requestClose: () => handle.requestClose(),
145
208
  requestBack: () => handle.requestBack(),
209
+ activateNavigation: (activation) => handle.activateNavigation(activation),
146
210
  activateStatus: (action) => handle.activateStatus(action),
147
211
  dispose: close,
148
212
  };
@@ -253,6 +253,7 @@ export declare function useAppWindowManager(host: TourtoisAppSurfaceHost | null,
253
253
  }>[];
254
254
  }>) => Promise<void>;
255
255
  back: (windowId: string) => Promise<boolean>;
256
+ activateNavigation: (windowId: string, activation: import("@fungi.computer/agar").AppNavigationActivation) => Promise<boolean>;
256
257
  activateStatus: (windowId: string, action: import("@fungi.computer/agar").AppStatusAction) => Promise<boolean>;
257
258
  registerComputerChoice: (windowId: string, handler: () => void) => () => void;
258
259
  dispose: () => Promise<void>;
@@ -315,6 +316,33 @@ export declare function useAppWindowManager(host: TourtoisAppSurfaceHost | null,
315
316
  navigation: Readonly<{
316
317
  canGoBack: boolean;
317
318
  title?: string;
319
+ crumbs?: readonly Readonly<{
320
+ id: string;
321
+ label: string;
322
+ siblings?: readonly Readonly<{
323
+ id: string;
324
+ label: string;
325
+ }>[];
326
+ }>[];
327
+ primary?: readonly Readonly<{
328
+ id: string;
329
+ label: string;
330
+ variant?: "primary" | "outline" | "ghost";
331
+ tone?: "destructive";
332
+ disabled?: boolean;
333
+ }>[];
334
+ menu?: readonly Readonly<{
335
+ id: string;
336
+ label: string;
337
+ variant?: "primary" | "outline" | "ghost";
338
+ tone?: "destructive";
339
+ disabled?: boolean;
340
+ }>[];
341
+ status?: readonly Readonly<{
342
+ id: string;
343
+ label: string;
344
+ tone?: "neutral" | "info" | "warning" | "success" | "error";
345
+ }>[];
318
346
  }> | null;
319
347
  statusItems: readonly import("@fungi.computer/agar").AppStatusItem[];
320
348
  }>[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/tourtois",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "author": "Levi <levi@technomancy.dev>",
5
5
  "description": "Tourtois: the trusted desktop shell for Oyster and Fungi, with host-supplied Settings sections",
6
6
  "type": "module",
@@ -69,19 +69,19 @@
69
69
  "LICENSE"
70
70
  ],
71
71
  "dependencies": {
72
- "@fungi.computer/agar": "0.1.0",
72
+ "@fungi.computer/agar": "0.2.0",
73
73
  "@fungi.computer/cairn": "0.0.1",
74
- "@fungi.computer/caps": "0.0.2",
75
- "@fungi.computer/gills": "0.0.1",
74
+ "@fungi.computer/caps": "0.1.0",
75
+ "@fungi.computer/gills": "0.1.0",
76
76
  "@fungi.computer/maosaico": "0.0.1",
77
- "@fungi.computer/oyster": "0.1.0",
78
- "@fungi.computer/parakeet": "0.0.1",
79
- "@fungi.computer/puffball": "0.0.2",
80
- "@fungi.computer/shiitake": "0.1.0",
81
- "@fungi.computer/sigils": "0.0.1",
82
- "@fungi.computer/sprite": "0.0.1",
83
- "@fungi.computer/sprite-zdog": "0.0.1",
84
- "@fungi.computer/whistle": "0.0.1",
77
+ "@fungi.computer/oyster": "0.2.0",
78
+ "@fungi.computer/parakeet": "0.1.0",
79
+ "@fungi.computer/puffball": "0.1.0",
80
+ "@fungi.computer/shiitake": "0.2.0",
81
+ "@fungi.computer/sigils": "0.1.0",
82
+ "@fungi.computer/sprite": "0.1.0",
83
+ "@fungi.computer/sprite-zdog": "0.1.0",
84
+ "@fungi.computer/whistle": "0.0.2",
85
85
  "@tanstack/history": "1.162.1",
86
86
  "@tanstack/react-form": "1.33.2",
87
87
  "@tanstack/react-query": "5.102.8",
@@ -98,8 +98,8 @@
98
98
  },
99
99
  "devDependencies": {
100
100
  "@cloudflare/workers-types": "5.20260828.1",
101
- "@fungi.computer/forage": "0.0.2",
102
- "@fungi.computer/mycelium": "0.0.2",
101
+ "@fungi.computer/forage": "0.1.0",
102
+ "@fungi.computer/mycelium": "0.1.0",
103
103
  "@ladle/react": "5.1.1",
104
104
  "@types/react": "18.3.3",
105
105
  "@vitejs/plugin-react": "6.1.1",
@@ -119,7 +119,7 @@
119
119
  "fungi": {
120
120
  "app": {
121
121
  "displayName": "Tourtois",
122
- "icon": "installApps",
122
+ "icon": "tourtois",
123
123
  "capabilities": [
124
124
  "team.workbenchHost",
125
125
  "app.storage"