- Add Journal.AI project with LLamaSharp-based AI service (Phi-3 model) - Implement coach sessions (daily check-in, evening review, weekly review) - Add conversation CRUD with SQLCipher persistence - AI chat with full conversation history for context-aware replies - Frontend: CoachPanel, AI stores, conversation stores, side panel UI - Conversation list with create, rename, and delete support - Fix Phi-3 output quality (system prompt leaking, token cleanup, JSON filtering) - Fix CREATEDRAFT kind override in coach sessions Co-Authored-By: Oz <oz-agent@warp.dev>
195 lines
4.4 KiB
Svelte
195 lines
4.4 KiB
Svelte
<!-- @format -->
|
|
<script lang="ts">
|
|
export let activeSection: string | null = "entries";
|
|
export let onSelect: (id: string) => void = () => {};
|
|
|
|
type NavItem = {
|
|
id: string;
|
|
label: string;
|
|
icon: string;
|
|
};
|
|
|
|
const workspaceItems: NavItem[] = [
|
|
{ id: "entries", label: "Entries", icon: "menu_book" },
|
|
{ id: "calendar", label: "Calendar", icon: "calendar_month" },
|
|
{ id: "fragments", label: "Fragments", icon: "auto_stories" },
|
|
{ id: "todos", label: "To-Do List", icon: "checklist" },
|
|
{ id: "lists", label: "Lists", icon: "lists" },
|
|
{ id: "coach", label: "Coach", icon: "psychology" },
|
|
];
|
|
|
|
function selectItem(id: string) {
|
|
onSelect(id);
|
|
}
|
|
</script>
|
|
|
|
<aside class="navbar" aria-label="Primary navigation">
|
|
<div class="navbar-header">
|
|
<img src="svelte.svg" alt="Journal logo" />
|
|
</div>
|
|
|
|
<nav class="nav-groups" aria-label="Journal sections">
|
|
<div class="nav-group">
|
|
{#each workspaceItems as item}
|
|
<button
|
|
type="button"
|
|
class="nav-button"
|
|
class:is-active={activeSection === item.id}
|
|
on:click={() => selectItem(item.id)}
|
|
aria-label={item.label}
|
|
>
|
|
<span class="material-symbols-outlined">{item.icon}</span>
|
|
<span class="nav-tooltip" role="tooltip">{item.label}</span>
|
|
</button>
|
|
{/each}
|
|
</div>
|
|
</nav>
|
|
|
|
<button
|
|
type="button"
|
|
class="settings-chip"
|
|
class:is-active={activeSection === "settings"}
|
|
aria-label="Settings"
|
|
on:click={() => selectItem("settings")}
|
|
>
|
|
<span class="material-symbols-outlined">settings</span>
|
|
<span class="nav-tooltip" role="tooltip">Settings</span>
|
|
</button>
|
|
</aside>
|
|
|
|
<style>
|
|
.navbar {
|
|
position: relative;
|
|
z-index: 20;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 14px;
|
|
padding: 14px 10px;
|
|
background: linear-gradient(
|
|
180deg,
|
|
var(--surface-2) 0%,
|
|
var(--bg-navbar) 100%
|
|
);
|
|
border-right: 1px solid var(--border-soft);
|
|
}
|
|
|
|
.navbar-header img {
|
|
width: 34px;
|
|
height: 34px;
|
|
object-fit: cover;
|
|
border-radius: 9px;
|
|
border: 1px solid var(--border-strong);
|
|
display: block;
|
|
}
|
|
|
|
.nav-groups {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.nav-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
align-items: center;
|
|
}
|
|
|
|
.nav-button,
|
|
.settings-chip {
|
|
position: relative;
|
|
width: 44px;
|
|
height: 44px;
|
|
display: grid;
|
|
place-items: center;
|
|
border-radius: 10px;
|
|
color: var(--text-dim);
|
|
border: 1px solid transparent;
|
|
cursor: pointer;
|
|
transition:
|
|
background-color 0.14s ease,
|
|
color 0.14s ease,
|
|
border-color 0.14s ease;
|
|
}
|
|
|
|
.nav-button .material-symbols-outlined {
|
|
font-size: 1.18rem;
|
|
}
|
|
|
|
.settings-chip .material-symbols-outlined {
|
|
font-size: 1.18rem;
|
|
}
|
|
|
|
.nav-tooltip {
|
|
position: absolute;
|
|
left: calc(100% + 12px);
|
|
top: 50%;
|
|
transform: translateY(-50%) translateX(-4px);
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
white-space: nowrap;
|
|
padding: 4px 9px;
|
|
border-radius: 6px;
|
|
font-size: 0.76rem;
|
|
font-weight: 500;
|
|
color: var(--text-primary);
|
|
background: var(--surface-1);
|
|
border: 1px solid var(--border-strong);
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
|
|
transition:
|
|
opacity 0.12s ease,
|
|
transform 0.12s ease;
|
|
}
|
|
|
|
.nav-button:hover,
|
|
.nav-button:focus-visible,
|
|
.settings-chip:hover,
|
|
.settings-chip:focus-visible {
|
|
color: var(--text-primary);
|
|
background: var(--bg-hover);
|
|
border-color: var(--border-soft);
|
|
}
|
|
|
|
.nav-button:hover .nav-tooltip,
|
|
.nav-button:focus-visible .nav-tooltip,
|
|
.settings-chip:hover .nav-tooltip,
|
|
.settings-chip:focus-visible .nav-tooltip {
|
|
opacity: 1;
|
|
transform: translateY(-50%) translateX(0);
|
|
}
|
|
|
|
.nav-button.is-active {
|
|
color: var(--text-primary);
|
|
background: var(--bg-active);
|
|
border-color: var(--border-strong);
|
|
}
|
|
|
|
.nav-button.is-active .material-symbols-outlined {
|
|
color: var(--accent);
|
|
}
|
|
|
|
.settings-chip {
|
|
margin-top: auto;
|
|
}
|
|
|
|
.settings-chip.is-active {
|
|
color: var(--text-primary);
|
|
background: var(--bg-active);
|
|
border-color: var(--border-strong);
|
|
}
|
|
|
|
.settings-chip.is-active .material-symbols-outlined {
|
|
color: var(--accent);
|
|
}
|
|
|
|
@media (max-width: 980px) {
|
|
.nav-button,
|
|
.settings-chip {
|
|
width: 40px;
|
|
height: 40px;
|
|
}
|
|
}
|
|
</style>
|