Merge branch 'work/companion' into hare/develop

This commit is contained in:
2026-09-03 02:39:54 +09:00
4 changed files with 108 additions and 26 deletions
@@ -812,6 +812,12 @@ Deno.test("Account UI owns browser passkey session state without workspace autho
const globalSidebar = await Deno.readTextFile(
new URL("../sidebar/GlobalSidebar.svelte", import.meta.url),
);
const globalNavSections = await Deno.readTextFile(
new URL("../sidebar/GlobalNavSections.svelte", import.meta.url),
);
const workspaceCatalogPage = await Deno.readTextFile(
new URL("./../../../routes/+page.svelte", import.meta.url),
);
const sidebarFrame = await Deno.readTextFile(
new URL("../sidebar/SidebarFrame.svelte", import.meta.url),
);
@@ -874,13 +880,24 @@ Deno.test("Account UI owns browser passkey session state without workspace autho
"Root layout chrome should keep GlobalSidebar as the root slot owner while account navigation stays in the header",
);
assert(
globalSidebar.includes('aria-label="Global pages"') &&
!globalSidebar.includes('<p class="sidebar-section-label">') &&
globalSidebar.includes("/account") &&
globalSidebar.includes("/login/device") &&
!globalSidebar.includes("Tickets") &&
!globalSidebar.includes("Repositories"),
"Root default sidebar should contain only global navigation, not workspace-scoped sections",
globalSidebar.includes("GlobalNavSections") &&
globalNavSections.includes('aria-label="Global pages"') &&
!globalNavSections.includes('<p class="sidebar-section-label">') &&
globalNavSections.includes('"/account"') &&
globalNavSections.includes('"/login/device"') &&
!globalNavSections.includes('label: "Workspaces"') &&
globalNavSections.includes("sidebar-nav-section--category") &&
globalNavSections.includes("global-workspaces-heading") &&
globalNavSections.includes("workspaces") &&
globalNavSections.includes("workspace.display_name") &&
globalNavSections.includes("workspaceHref(workspace.workspace_id)") &&
workspaceCatalogPage.includes("SidebarOverride") &&
workspaceCatalogPage.includes("sidebar={homeSidebar}") &&
workspaceCatalogPage.includes("GlobalNavSections") &&
workspaceCatalogPage.includes("{workspaces}") &&
!globalNavSections.includes("Tickets") &&
!globalNavSections.includes("Repositories"),
"Root page sidebar should replace the Workspaces button with a categorized accessible Workspace list below the remaining global navigation",
);
assert(
workspaceLayout.includes("{#snippet workspaceSidebar()}") &&
@@ -0,0 +1,70 @@
<script lang="ts">
import type { WorkspaceCatalogRecord } from "$lib/workspace/api/workspace-catalog";
type Props = {
currentPath: string;
workspaces?: WorkspaceCatalogRecord[] | null;
workspaceError?: string | null;
};
let {
currentPath,
workspaces = null,
workspaceError = null,
}: Props = $props();
const globalItems = [
{ href: "/#workspace-create-title", label: "Create Workspace" },
{ href: "/account", label: "Account" },
{ href: "/login/device", label: "Device Login" },
];
function workspaceHref(workspaceId: string): string {
return `/w/${encodeURIComponent(workspaceId)}`;
}
</script>
<nav class="sidebar-sections" aria-label="Global pages">
<section class="sidebar-nav-section">
<div class="sidebar-list">
{#each globalItems as item}
<a
class="sidebar-link"
class:active={currentPath === item.href}
href={item.href}
aria-current={currentPath === item.href ? "page" : undefined}
>
<span>{item.label}</span>
</a>
{/each}
</div>
</section>
{#if workspaces !== null}
<section class="sidebar-nav-section sidebar-nav-section--category" aria-labelledby="global-workspaces-heading">
<h2 id="global-workspaces-heading" class="sidebar-nav-section__header">workspaces</h2>
{#if workspaceError}
<p class="workspace-status error">Workspace list unavailable.</p>
{/if}
{#if workspaces.length > 0}
<div class="sidebar-list">
{#each workspaces as workspace (workspace.workspace_id)}
{@const href = workspaceHref(workspace.workspace_id)}
<a
class="sidebar-link"
class:active={currentPath === href}
{href}
aria-current={currentPath === href ? "page" : undefined}
>
<span>{workspace.display_name}</span>
</a>
{/each}
</div>
{:else if !workspaceError}
<p class="workspace-status">No accessible Workspaces.</p>
{/if}
</section>
{/if}
</nav>
@@ -1,5 +1,6 @@
<script lang="ts">
import type { SidebarSnippet } from './context';
import GlobalNavSections from './GlobalNavSections.svelte';
import './sidebar.css';
type Props = {
@@ -8,13 +9,6 @@
};
const { currentPath, content = null }: Props = $props();
const items = [
{ href: '/', label: 'Workspaces' },
{ href: '/#workspace-create-title', label: 'Create Workspace' },
{ href: '/account', label: 'Account' },
{ href: '/login/device', label: 'Device Login' },
];
</script>
{#if content}
@@ -22,18 +16,7 @@
{:else}
<div class="global-sidebar" aria-label="Global navigation">
<div class="global-sidebar-section">
<nav class="sidebar-list" aria-label="Global pages">
{#each items as item}
<a
class="sidebar-link"
class:active={currentPath === item.href}
href={item.href}
aria-current={currentPath === item.href ? 'page' : undefined}
>
<span>{item.label}</span>
</a>
{/each}
</nav>
<GlobalNavSections {currentPath} />
</div>
</div>
{/if}
+12
View File
@@ -8,6 +8,8 @@
type CreateWorkspaceRequest,
type WorkspaceCatalogItem,
} from "$lib/workspace/api/workspace-catalog";
import GlobalNavSections from "$lib/workspace/sidebar/GlobalNavSections.svelte";
import SidebarOverride from "$lib/workspace/sidebar/SidebarOverride.svelte";
import "$lib/workspace/styles/workspace-catalog.css";
let { data } = $props();
@@ -87,6 +89,16 @@
}
</script>
{#snippet homeSidebar()}
<GlobalNavSections
currentPath="/"
{workspaces}
workspaceError={catalogError}
/>
{/snippet}
<SidebarOverride sidebar={homeSidebar} />
<svelte:head>
<title>Workspaces · Yoi</title>
</svelte:head>