Skip to content

Commit ea69ce5

Browse files
authored
Merge pull request #34 from menloresearch/justrach/new-settings-page
Justrach/new settings page
2 parents f2fe23c + 0a0e81d commit ea69ce5

File tree

15 files changed

+1592
-688
lines changed

15 files changed

+1592
-688
lines changed

PR.md

Lines changed: 69 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,81 @@
1-
Title: UX Improvements: @mention Tab Selection, LIFO Sorting, Full-Screen Overlay Menu + Jan API Default
1+
Title: Side Panel UX + Settings Page — Composer Refactor, Suggestion Pills, Theming, and New Components
22

33
Summary
4-
- Fix @mention tab selection bug preventing tabs from being added to context via chat input
5-
- Implement LIFO (Last-In-First-Out) sorting for Add context popup to prioritize recent tabs
6-
- Redesign hamburger menu as full-screen overlay with animated slide-in panel
7-
- Set Jan API (https://api.jan.ai/v1) as default endpoint with jan-v1-4b model
8-
- Update documentation with new UX patterns and behavior
4+
- Introduces a dedicated Settings panel in the side panel UI to configure provider, model, and behavior without leaving the flow.
5+
- Adds Suggestion Pills for quick-start prompts and common actions in the composer.
6+
- Refactors Composer and ComposerInput for cleaner structure, better shortcuts, and more predictable behavior.
7+
- Adds Tooltip and ScrollArea UI components; standardizes global theming with new tokens and a Settings icon.
8+
- Tightens auto-follow active tab behavior in `App.jsx` and streamlines state management.
99

10-
What's included
11-
- **@mention Tab Selection Fix**
12-
- Fixed undefined `insertMentionTab` references by wiring to existing `handleMentionSelect`
13-
- Tab selection now adds to context AND keeps mention text in input with proper cursor positioning
14-
- Supports multiple @mentions in single message
15-
- **LIFO Tab Sorting**
16-
- Add context popup now shows unpinned tabs first, then by lastAccessed (desc), fallback to index (desc)
17-
- Improves tab selection UX when working with many open tabs
18-
- Enhanced search includes title, URL, and tab ID matching
19-
- **Full-Screen Overlay Menu**
20-
- Hamburger menu opens as fixed overlay with semi-transparent scrim covering conversation area
21-
- 320px animated slide-in panel with click-outside-to-close behavior
22-
- Smooth motion animations and proper z-index layering
23-
- **Jan API as Default**
24-
- Default provider changed from "custom" to "jan"
25-
- Default endpoint: https://api.jan.ai/v1 (was empty)
26-
- Default model: jan-v1-4b (was empty)
27-
- API key disabled by default (useApiKey: false)
28-
- **Documentation Updates**
29-
- New ADR-003 documenting UX improvement decisions and implementation
30-
- Updated behavior.md with @mention and overlay menu patterns
31-
- Enhanced considerations.md with new UX scenarios
10+
What’s Included
11+
- Side Panel
12+
- Settings panel UI (`ui/sidepanel/components/Settings.jsx`).
13+
- Suggestion pills component (`ui/sidepanel/components/SuggestionPills.jsx`).
14+
- Add Context modal (`ui/sidepanel/components/AddContextModal.jsx`).
15+
- Composer refactors (`ui/sidepanel/components/composer/Composer.jsx`, `ui/sidepanel/components/ComposerInput.jsx`).
16+
- App state/auto-follow adjustments (`ui/sidepanel/App.jsx`).
17+
- UI Components
18+
- Tooltip (`ui/components/ui/tooltip.tsx`) and ScrollArea (`ui/components/ui/scroll-area.tsx`).
19+
- Button and Input refinements (`ui/components/ui/button.jsx`, `ui/components/ui/input.jsx`).
20+
- Settings icon (`ui/sidepanel/components/icons/SettingsIcon.jsx`).
21+
- Theming & Styles
22+
- Theme tokens (`ui/themes.css`) and global tweaks (`ui/styles.css`).
3223

3324
Rationale
34-
- **Productivity**: LIFO tab sorting reduces time to find relevant tabs in multi-tab workflows
35-
- **Functionality**: @mention tab selection was broken due to undefined function references
36-
- **Visual Hierarchy**: Full-screen overlay provides clear navigation context vs narrow sidebar
37-
- **Onboarding**: Jan API default eliminates initial setup friction for new users
38-
- **Consistency**: Maintains design system tokens and theme compatibility
25+
- Improve onboarding and control with an integrated Settings panel.
26+
- Speed common tasks with Suggestion Pills and better keyboard ergonomics.
27+
- Prepare for future UI scale with reusable Tooltip/ScrollArea primitives.
28+
- Consolidate theme tokens for consistent styling and easier iteration.
3929

40-
Files changed
41-
- src/background.js (default settings, Jan API endpoints)
42-
- ui/sidepanel/App.jsx (mention fix, LIFO sorting, overlay menu)
43-
- docs/adr-003-ux-improvements.md (new ADR)
44-
- behavior.md (@mention and overlay documentation)
45-
- considerations.md (UX scenarios)
30+
How to Test
31+
1) Build and load the extension
32+
- `npm run build` (or `bun run build`) and load `dist/` via `chrome://extensions` → Load unpacked.
33+
2) Settings Panel
34+
- Open side panel → gear icon. Verify provider, model, and options render and persist.
35+
- Toggle relevant options and confirm they affect new sessions where applicable.
36+
3) Suggestion Pills
37+
- In the side panel composer, confirm pills render and insert text/context when clicked.
38+
- Verify keyboard focus/selection works with Tab/Enter.
39+
4) Composer/ComposerInput
40+
- Type, submit, and edit messages. Confirm shortcuts (Enter to send, Shift+Enter newline) behave consistently.
41+
- Validate streaming, cancel, and retry work without focus loss.
42+
5) Auto-follow Active Tab
43+
- Switch browser tabs with auto-follow enabled; confirm session follows active tab without losing state.
44+
6) Tooltip/ScrollArea
45+
- Confirm tooltips render on hover/focus in updated UI. Validate long lists scroll smoothly.
46+
7) Theming/Styles
47+
- Sanity-check themes and global styles; ensure contrast and spacing look consistent.
4648

47-
How to test locally
48-
1) **@mention Tab Selection**
49-
- Type `@` in chat input to trigger mention popup
50-
- Verify tabs appear in LIFO order (unpinned first, then by lastAccessed desc)
51-
- Use arrow keys to navigate, Enter/Tab/click to select
52-
- Confirm tab is added to context AND mention text remains in input
53-
54-
2) **Add Context Popup LIFO Sorting**
55-
- Click "Add context" button
56-
- Verify tabs show unpinned first, then most recently accessed
57-
- Search should match title, URL, and tab ID
58-
59-
3) **Full-Screen Overlay Menu**
60-
- Click hamburger menu (☰)
61-
- Verify full-screen overlay with scrim covers conversation area
62-
- Test click-outside-to-close behavior
63-
- Check smooth slide-in animation
64-
65-
4) **Jan API Default**
66-
- Fresh install should default to Jan provider with https://api.jan.ai/v1
67-
- Model should default to jan-v1-4b
68-
- API key should be disabled by default
49+
Files Touched (high-level)
50+
- `ui/sidepanel/App.jsx`
51+
- `ui/sidepanel/components/ComposerInput.jsx`
52+
- `ui/sidepanel/components/composer/Composer.jsx`
53+
- `ui/sidepanel/components/SuggestionPills.jsx`
54+
- `ui/sidepanel/components/AddContextModal.jsx`
55+
- `ui/sidepanel/components/Settings.jsx`
56+
- `ui/components/ui/button.jsx`, `ui/components/ui/input.jsx`
57+
- `ui/components/ui/tooltip.tsx`, `ui/components/ui/scroll-area.tsx`
58+
- `ui/sidepanel/components/icons/SettingsIcon.jsx`
59+
- `ui/themes.css`, `ui/styles.css`
6960

70-
5) **Build and Load Extension**
71-
- `npm run build` then load `dist/` via `chrome://extensions` → Load unpacked
72-
- Test all above functionality in browser
61+
Behavioral Notes
62+
- Auto-follow reads from `context.autoFollowActiveTab`; side panel re-registers active tab as needed.
63+
- Composer shortcuts and focus handling are more predictable; streaming remains cancellable.
7364

74-
Behavioral Changes
75-
- **@mention**: Now functional - adds tabs to context while preserving mention text
76-
- **Tab Sorting**: LIFO ordering prioritizes recently accessed tabs for faster selection
77-
- **Menu UX**: Full-screen overlay provides better visual hierarchy than narrow sidebar
78-
- **Default Config**: Jan API eliminates setup friction for new users
79-
80-
Technical Implementation
81-
- Fixed undefined function references in mention system
82-
- Added LIFO sort logic with unpinned-first priority
83-
- Implemented fixed-position overlay with scrim and animations
84-
- Updated default settings in background.js
85-
- Preserved theme system compatibility
65+
Screenshots
66+
- Add Settings panel, Suggestion Pills, and composer before/after screenshots (attach).
8667

8768
Checklist
88-
- [x] @mention tab selection adds to context and keeps text in input
89-
- [x] Add context popup shows LIFO-sorted tabs
90-
- [x] Hamburger menu opens as full-screen overlay with animations
91-
- [x] Jan API set as default endpoint with jan-v1-4b model
92-
- [x] Documentation updated (ADR-003, behavior.md, considerations.md)
93-
- [x] Build succeeds without errors
69+
- [x] Builds cleanly and loads in Chromium.
70+
- [x] Settings persist and apply in new sessions.
71+
- [x] Suggestion pills insert as expected; accessible via keyboard.
72+
- [x] Composer shortcuts correct; streaming is responsive and cancellable.
73+
- [x] Tooltip/ScrollArea render and behave consistently.
74+
- [x] Theming tokens apply without regressions.
75+
76+
Breaking Changes
77+
- None expected. UI-only additions and refactors; no storage schema changes.
78+
79+
Release Notes
80+
- Side panel Settings, Suggestion Pills, and composer refactors improve UX and configurability. New Tooltip/ScrollArea primitives and theme tokens standardize UI going forward.
9481

docs/AGENTS_GUIDE.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Jan Extension — Agents Guide
2+
3+
What powers the Jan Extension under the hood: agents, message flow, and how to extend it.
4+
5+
## Goals
6+
7+
- Unify page summarization, inline writing assist, and quick web search.
8+
- Support any OpenAI-compatible API (Jan Server/local, Cerebras, OpenAI, etc.).
9+
- Keep UX fast and predictable across content script, background, and side panel.
10+
11+
## Mental Model
12+
13+
- Background (service worker): the router/orchestrator. Hosts agents and tools.
14+
- Content script: runs in the page. Extracts text, shows inline tooltip UI, DOM ops.
15+
- Side panel UI: the app surface. Shows sessions, chat composer, and streaming output.
16+
17+
These talk over a long‑lived port so streaming is smooth and cancellable.
18+
19+
## Key Components
20+
21+
- Side Panel UI (`ui/sidepanel/`)
22+
- Shows session context + chat composer; renders streaming Markdown.
23+
- Tracks active tab; auto-follows when enabled.
24+
- Background Service Worker (`src/background.js`)
25+
- Central router for UI ↔ content scripts ↔ external services.
26+
- Hosts agents/tools including Google Search + SERP scraping and the MCP bridge.
27+
- Content Script (`src/content.js`)
28+
- Extracts page/selection text; presents Inline Assistant tooltip; DOM helpers.
29+
- Options UI (`ui/options/`)
30+
- Configure provider base URL, API key, model presets (OpenAI‑compatible endpoints, including Jan and Cerebras).
31+
- MCP Search Bridge (`mcp/search-server/`)
32+
- Optional local MCP server that exposes search/visit tools to LLM clients via a WebSocket bridge.
33+
34+
## Agents
35+
36+
- Page Summarizer (Side Panel)
37+
- Goal: Summarize the current page or selection with clear, skimmable Markdown.
38+
- Inputs: Page text, selection (optional), provider config, session settings.
39+
- Output: Markdown with headings, bullets, and links.
40+
41+
- Inline Assistant
42+
- Goal: Improve selected text inline (rewrite, simplify, translate, adjust tone).
43+
- Entrypoint: Tooltip near selection; keyboard shortcuts to apply/copy/regenerate.
44+
- Messages are built by `buildInlineAssistMessages` in `src/background.js`.
45+
46+
- Search (Google Search + Scrape)
47+
- Goal: Run a quick web search, scrape the SERP for links/snippets.
48+
- Entrypoint: `performGoogleSearchAndScrape(payload)` in `src/background.js`.
49+
- Flow: Opens an inactive Google tab, waits for hydration, extracts structured results, optionally closes the tab.
50+
51+
- MCP Bridge Agent
52+
- Goal: Expose `search` and `visit_tool` over MCP so local tools are accessible from LLM apps.
53+
- Lives in `mcp/search-server/` (TypeScript). Structured outputs are planned with Zod.
54+
55+
## Message & Port Routing
56+
57+
- Long‑lived port: Side panel registers a persistent port with background.
58+
- On tab activation, the side panel re‑registers the active `tabId` via `{ type: 'REGISTER_PORT', tabId }` so streaming routes to the right place.
59+
- Session ↔ tab mapping: Background maintains a tab→session map; side panel switches sessions as the active tab changes.
60+
- Recent fix: Auto‑follow reads from the target session’s `context.autoFollowActiveTab` (see `ui/sidepanel/App.jsx`).
61+
- Content script messaging: Background requests page/selection text; content script returns selection/caret details and hosts the inline UI.
62+
63+
## Core Flows
64+
65+
- Summarize Current Page (Side Panel)
66+
1) Side panel asks content script for page/selection data.
67+
2) Background builds the LLM prompt and streams to the provider.
68+
3) Side panel renders streaming Markdown; user can copy/export.
69+
70+
- Inline Assistant
71+
1) User selects text; content script shows the tooltip.
72+
2) Background builds messages (`buildInlineAssistMessages`) and calls the provider.
73+
3) Content script previews the result; user Apply/Copy/Regenerate.
74+
75+
- Google Search + Scrape
76+
1) UI or an agent calls `performGoogleSearchAndScrape({ query, closeTab? })`.
77+
2) Background opens an inactive Google tab, waits for load + hydration, then scrapes.
78+
3) Returns structured JSON; URLs are also mirrored to `_meta.urls` for compatibility.
79+
80+
## Extending Agents
81+
82+
- Add a tool/agent in `src/background.js`.
83+
- Define a message type and handler; wire it from the side panel or content script.
84+
- Prefer streaming when supported; keep UI cancellable and responsive.
85+
86+
## Permissions & Privacy
87+
88+
- Manifest (`manifest.json`) requests `tabs`, `activeTab`, `sidePanel`, `storage`, and wide host permissions for dev.
89+
- For production, restrict host permissions and sanitize logs.
90+
- Do not persist secrets; users configure API keys in Options.
91+
92+
## References
93+
94+
- Background: `src/background.js`
95+
- Content: `src/content.js`
96+
- Side Panel: `ui/sidepanel/`
97+
- Options: `ui/options/`
98+
- MCP Server: `mcp/search-server/`
99+
100+
Note: This guide mirrors the root-level `agents.md` for convenience and discoverability under `docs/`.

ui/components/ui/button.jsx

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ import { cn } from '../../lib/utils'
33

44
export function Button({ className, variant = 'default', size = 'default', asChild = false, ...props }) {
55
const Comp = asChild ? 'span' : 'button'
6-
const base = 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[--brand-blue] disabled:pointer-events-none disabled:opacity-50'
6+
const base = 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50'
7+
const focusRing = 'focus-visible:ring-[--theme-primary]'
78
const variants = {
8-
default: 'bg-[--brand-amber] text-white hover:bg-[--brand-amber-2]',
9-
secondary: 'bg-neutral-100 text-neutral-900 hover:bg-neutral-200',
10-
ghost: 'bg-transparent hover:bg-black/5',
11-
outline: 'border border-neutral-300 hover:bg-black/5',
9+
default: 'text-white hover:opacity-90',
10+
secondary: 'hover:opacity-90',
11+
ghost: 'bg-transparent hover:opacity-80',
12+
outline: 'border hover:opacity-90',
1213
pastel: 'btn-pastel border border-transparent',
1314
pastelReverse: 'btn-pastel-rev border border-transparent',
1415
}
@@ -18,5 +19,24 @@ export function Button({ className, variant = 'default', size = 'default', asChi
1819
lg: 'h-10 px-4',
1920
icon: 'h-8 w-8 p-0',
2021
}
21-
return <Comp className={cn(base, variants[variant] || variants.default, sizes[size] || sizes.default, className)} {...props} />
22+
const getVariantStyles = (variant) => {
23+
switch (variant) {
24+
case 'default':
25+
return { backgroundColor: 'var(--theme-primary)', color: 'var(--theme-primary-fg)' }
26+
case 'secondary':
27+
return { backgroundColor: 'var(--theme-secondary)', color: 'var(--theme-secondary-fg)' }
28+
case 'ghost':
29+
return { backgroundColor: 'transparent', color: 'var(--theme-high-em-text)' }
30+
case 'outline':
31+
return { backgroundColor: 'var(--theme-tertiary)', color: 'var(--theme-tertiary-fg)', borderColor: 'var(--theme-border-interactive)' }
32+
default:
33+
return { backgroundColor: 'var(--theme-primary)', color: 'var(--theme-primary-fg)' }
34+
}
35+
}
36+
37+
return <Comp
38+
className={cn(base, focusRing, variants[variant] || variants.default, sizes[size] || sizes.default, className)}
39+
style={getVariantStyles(variant)}
40+
{...props}
41+
/>
2242
}

ui/components/ui/input.jsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,24 @@ export const Input = React.forwardRef(function Input(
1010
ref={ref}
1111
type={type}
1212
className={cn(
13-
'flex h-9 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground',
14-
'placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
15-
'disabled:cursor-not-allowed disabled:opacity-50',
13+
'flex h-9 w-full rounded-md border px-3 py-2 text-sm',
14+
'focus-visible:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-50',
1615
className
1716
)}
17+
style={{
18+
backgroundColor: 'var(--theme-field)',
19+
borderColor: 'var(--theme-border-interactive)',
20+
color: 'var(--theme-high-em-text)',
21+
'--placeholder-color': 'var(--theme-field-fg)'
22+
}}
23+
onFocus={(e) => {
24+
e.target.style.backgroundColor = 'var(--theme-field-active)'
25+
e.target.style.borderColor = 'var(--theme-primary)'
26+
}}
27+
onBlur={(e) => {
28+
e.target.style.backgroundColor = 'var(--theme-field)'
29+
e.target.style.borderColor = 'var(--theme-border-interactive)'
30+
}}
1831
{...props}
1932
/>
2033
)

ui/components/ui/scroll-area.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react'
2+
3+
// Simple scroll area components - basic implementations for the extension
4+
export const ScrollArea = {
5+
Root: ({ children, className, ...props }) => (
6+
<div className={`relative overflow-hidden ${className || ''}`} {...props}>
7+
{children}
8+
</div>
9+
),
10+
11+
Viewport: ({ children, className, ...props }) => (
12+
<div className={`h-full w-full overflow-auto ${className || ''}`} {...props}>
13+
{children}
14+
</div>
15+
),
16+
17+
Scrollbar: ({ children, className, orientation = 'vertical', ...props }) => (
18+
<div
19+
className={`flex select-none touch-none bg-transparent ${className || ''}`}
20+
data-orientation={orientation}
21+
{...props}
22+
>
23+
{children}
24+
</div>
25+
),
26+
27+
Thumb: ({ className, ...props }) => (
28+
<div className={`flex-1 rounded-full ${className || ''}`} {...props} />
29+
)
30+
}

ui/components/ui/tooltip.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react'
2+
3+
// Simple tooltip components - basic implementations for the extension
4+
export const Root = ({ children }) => <div>{children}</div>
5+
6+
export const Trigger = ({ children, asChild, ...props }) => {
7+
if (asChild && React.isValidElement(children)) {
8+
return React.cloneElement(children, props)
9+
}
10+
return <div {...props}>{children}</div>
11+
}
12+
13+
export const Content = ({ children, className, sideOffset, ...props }) => (
14+
<div
15+
className={`z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 ${className || ''}`}
16+
{...props}
17+
>
18+
{children}
19+
</div>
20+
)

0 commit comments

Comments
 (0)