Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a22098d661 | |||
| 69b17e15a2 | |||
| de279ef6bf | |||
| c6fed386ef | |||
| ffcd7d21b3 | |||
| efb7507a8f |
@@ -10,7 +10,7 @@
|
|||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"name": "claude-mem",
|
"name": "claude-mem",
|
||||||
"version": "6.3.4",
|
"version": "6.3.6",
|
||||||
"source": "./plugin",
|
"source": "./plugin",
|
||||||
"description": "Persistent memory system for Claude Code - context compression across sessions"
|
"description": "Persistent memory system for Claude Code - context compression across sessions"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,42 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
## [6.3.5] - 2025-11-30
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- ✨ Restored Discord community button in viewer header
|
||||||
|
- 📱 Added responsive mobile navigation menu
|
||||||
|
- 🔄 Reorganized Sidebar component for better mobile UX
|
||||||
|
- 🐛 Fixed missing props being passed to Sidebar component
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
- Community button visible in header on desktop (> 600px width)
|
||||||
|
- Mobile menu icon appears on small screens (≤ 600px width)
|
||||||
|
- Sidebar toggles via hamburger menu on mobile
|
||||||
|
- Both buttons positioned in header for consistent UX
|
||||||
|
|
||||||
|
Full changelog: https://github.com/thedotmack/claude-mem/compare/v6.3.4...v6.3.5
|
||||||
|
|
||||||
|
## [6.3.4] - 2025-11-30
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
### Worker Startup Improvements
|
||||||
|
|
||||||
|
Fixed critical issues with worker service startup on fresh installations:
|
||||||
|
|
||||||
|
- **Auto-start worker after installation** - The PM2 worker now starts automatically during plugin installation
|
||||||
|
- **Local PM2 resolution** - Plugin now uses local PM2 from node_modules/.bin instead of requiring global installation
|
||||||
|
- **Improved error messages** - Clear, actionable instructions with full paths when worker fails to start
|
||||||
|
- **Cross-platform support** - Proper handling of Windows platform differences (pm2.cmd)
|
||||||
|
- **Security enhancement** - Switched from execSync to spawnSync with array arguments to prevent command injection
|
||||||
|
|
||||||
|
These changes significantly improve the first-time installation experience, eliminating the need for manual PM2 setup.
|
||||||
|
|
||||||
|
**Special thanks to @dreamiurg for identifying and fixing this critical UX issue!** 🙏
|
||||||
|
|
||||||
## [6.3.3] - 2025-11-30
|
## [6.3.3] - 2025-11-30
|
||||||
|
|
||||||
Bug fixes and improvements to timeline context feature:
|
Bug fixes and improvements to timeline context feature:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Claude-mem is a Claude Code plugin providing persistent memory across sessions. It captures tool usage, compresses observations using the Claude Agent SDK, and injects relevant context into future sessions.
|
Claude-mem is a Claude Code plugin providing persistent memory across sessions. It captures tool usage, compresses observations using the Claude Agent SDK, and injects relevant context into future sessions.
|
||||||
|
|
||||||
**Current Version**: 6.3.4
|
**Current Version**: 6.3.6
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
|
|||||||
Generated
+4
@@ -1484,6 +1484,7 @@
|
|||||||
"integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==",
|
"integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
@@ -2337,6 +2338,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
||||||
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.8",
|
"accepts": "~1.3.8",
|
||||||
"array-flatten": "1.1.1",
|
"array-flatten": "1.1.1",
|
||||||
@@ -3849,6 +3851,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
},
|
},
|
||||||
@@ -4850,6 +4853,7 @@
|
|||||||
"node_modules/zod": {
|
"node_modules/zod": {
|
||||||
"version": "3.25.76",
|
"version": "3.25.76",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claude-mem",
|
"name": "claude-mem",
|
||||||
"version": "6.3.4",
|
"version": "6.3.6",
|
||||||
"description": "Memory compression system for Claude Code - persist context across sessions",
|
"description": "Memory compression system for Claude Code - persist context across sessions",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"claude",
|
"claude",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claude-mem",
|
"name": "claude-mem",
|
||||||
"version": "6.3.4",
|
"version": "6.3.6",
|
||||||
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
|
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Alex Newman"
|
"name": "Alex Newman"
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+421
-21
@@ -297,10 +297,9 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.full-height-flex-layout {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
width: 100vw;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,8 +313,9 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 400px;
|
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
background: var(--color-bg-primary);
|
background: var(--color-bg-primary);
|
||||||
border-left: 1px solid var(--color-border-primary);
|
border-left: 1px solid var(--color-border-primary);
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -331,12 +331,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
padding: 14px 18px;
|
padding: 16px 24px;
|
||||||
border-bottom: 1px solid var(--color-border-primary);
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: var(--color-bg-header);
|
background: linear-gradient(to bottom,
|
||||||
|
var(--color-bg-header) 0%,
|
||||||
|
var(--color-bg-primary) 100%);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
@@ -354,13 +358,124 @@
|
|||||||
color: var(--color-text-header);
|
color: var(--color-text-header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-community-btn {
|
||||||
|
display: none;
|
||||||
|
background: var(--color-bg-card);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 14px;
|
||||||
|
height: 36px;
|
||||||
|
cursor: pointer;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
white-space: nowrap;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
margin: 16px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-community-btn:hover {
|
||||||
|
background: var(--color-bg-card-hover);
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-community-btn:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.sidebar-community-btn {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-project-filter {
|
||||||
|
display: none;
|
||||||
|
padding: 16px 18px;
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-project-filter label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-project-filter select {
|
||||||
|
width: 100%;
|
||||||
|
background: var(--color-bg-card);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 32px 0 12px;
|
||||||
|
height: 36px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%23666' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 10px center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-project-filter select:hover {
|
||||||
|
background-color: var(--color-bg-card-hover);
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-project-filter select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
box-shadow: 0 0 0 3px rgba(9, 105, 218, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.sidebar-project-filter {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-social-links {
|
||||||
|
display: none;
|
||||||
|
padding: 16px 18px;
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-social-links .icon-link {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.sidebar-social-links {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.header h1 {
|
.header h1 {
|
||||||
font-size: 16px;
|
font-size: 17px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--color-text-header);
|
color: var(--color-text-header);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 12px;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -385,7 +500,6 @@
|
|||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-family: 'Monaspace Radon', monospace;
|
font-family: 'Monaspace Radon', monospace;
|
||||||
min-width: 18px;
|
|
||||||
height: 18px;
|
height: 18px;
|
||||||
border-radius: 9px;
|
border-radius: 9px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -409,43 +523,106 @@
|
|||||||
.logo-text {
|
.logo-text {
|
||||||
font-family: 'Monaspace Radon', monospace;
|
font-family: 'Monaspace Radon', monospace;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
font-size: 20px;
|
font-size: 21px;
|
||||||
letter-spacing: -0.03em;
|
letter-spacing: -0.03em;
|
||||||
color: var(--color-text-logo);
|
color: var(--color-text-logo);
|
||||||
|
line-height: 1;
|
||||||
|
padding-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 8px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-btn,
|
.settings-btn,
|
||||||
.theme-toggle-btn {
|
.theme-toggle-btn {
|
||||||
background: transparent;
|
background: var(--color-bg-card);
|
||||||
border: 1px solid var(--color-border-primary);
|
border: 1px solid var(--color-border-primary);
|
||||||
padding: 8px;
|
border-radius: 6px;
|
||||||
|
padding: 0;
|
||||||
width: 36px;
|
width: 36px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-secondary);
|
||||||
transition: all 0.15s ease;
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-btn:hover,
|
.settings-btn:hover,
|
||||||
.theme-toggle-btn:hover {
|
.theme-toggle-btn:hover {
|
||||||
background: var(--color-bg-secondary);
|
background: var(--color-bg-card-hover);
|
||||||
border-color: var(--color-border-focus);
|
border-color: var(--color-border-focus);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-btn.active {
|
.settings-btn.active {
|
||||||
background: var(--color-bg-button);
|
background: linear-gradient(135deg, var(--color-bg-button) 0%, var(--color-accent-primary) 100%);
|
||||||
border-color: var(--color-bg-button);
|
border-color: var(--color-bg-button);
|
||||||
color: var(--color-text-button);
|
color: var(--color-text-button);
|
||||||
|
box-shadow: 0 2px 8px rgba(9, 105, 218, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-btn {
|
||||||
|
background: var(--color-bg-card);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 14px;
|
||||||
|
height: 36px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
white-space: nowrap;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-btn:hover {
|
||||||
|
background: var(--color-bg-card-hover);
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-btn:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
background: var(--color-bg-card);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-radius: 6px;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-link:hover {
|
||||||
|
background: var(--color-bg-card-hover);
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-icon,
|
.settings-icon,
|
||||||
@@ -492,6 +669,40 @@
|
|||||||
transition: all 0.15s ease;
|
transition: all 0.15s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status select {
|
||||||
|
background: var(--color-bg-card);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 32px 0 12px;
|
||||||
|
height: 36px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%23666' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 10px center;
|
||||||
|
max-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status select:hover {
|
||||||
|
background-color: var(--color-bg-card-hover);
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
box-shadow: 0 0 0 3px rgba(9, 105, 218, 0.1);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
select:hover,
|
select:hover,
|
||||||
input:hover {
|
input:hover {
|
||||||
border-color: var(--color-border-focus);
|
border-color: var(--color-border-focus);
|
||||||
@@ -527,15 +738,15 @@
|
|||||||
|
|
||||||
.feed {
|
.feed {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: scroll;
|
||||||
|
height: 100vh;
|
||||||
padding: 24px 18px;
|
padding: 24px 18px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.feed-content {
|
.feed-content {
|
||||||
width: 100%;
|
max-width: 650px;
|
||||||
max-width: 42rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
@@ -579,7 +790,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
min-width: 10%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-subheading-left {
|
.card-subheading-left {
|
||||||
@@ -1190,6 +1400,196 @@
|
|||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Utility: Container */
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Tablet Responsive Styles - 481px to 768px */
|
||||||
|
@media (max-width: 768px) and (min-width: 481px) {
|
||||||
|
/* Header stays on one line, hide icon links to save space */
|
||||||
|
.header {
|
||||||
|
padding: 14px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status select {
|
||||||
|
max-width: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide icon links (docs, github, twitter) on tablet */
|
||||||
|
.icon-link {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar full width on tablet */
|
||||||
|
.sidebar {
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Feed adjustments */
|
||||||
|
.feed {
|
||||||
|
padding: 20px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feed-content {
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card adjustments */
|
||||||
|
.card {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile & Small Tablet - 600px and below */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
/* Hide community button in header, will show in sidebar */
|
||||||
|
.community-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Responsive Styles - 480px and below */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
/* Hide project dropdown in header, will show in sidebar */
|
||||||
|
.status select {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header stays on one line */
|
||||||
|
.header {
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 15px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logomark {
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
scrollbar-width: none;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status select {
|
||||||
|
max-width: 140px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0 28px 0 10px;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide icon links on mobile */
|
||||||
|
.icon-link {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-btn,
|
||||||
|
.theme-toggle-btn,
|
||||||
|
.icon-link {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-btn {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-btn svg {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-icon,
|
||||||
|
.theme-toggle-btn svg,
|
||||||
|
.icon-link svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar adjustments for mobile */
|
||||||
|
.sidebar {
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-header {
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Feed adjustments */
|
||||||
|
.feed {
|
||||||
|
padding: 16px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Card adjustments */
|
||||||
|
.card {
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header-left {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stats grid to single column */
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form inputs full width */
|
||||||
|
.form-group input,
|
||||||
|
.form-group select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scroll to top button position */
|
||||||
|
.scroll-to-top {
|
||||||
|
bottom: 16px;
|
||||||
|
right: 16px;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|||||||
+119
-16
@@ -51,10 +51,31 @@ function getPackageVersion() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNodeVersion() {
|
||||||
|
return process.version; // e.g., "v22.21.1"
|
||||||
|
}
|
||||||
|
|
||||||
function getInstalledVersion() {
|
function getInstalledVersion() {
|
||||||
try {
|
try {
|
||||||
if (existsSync(VERSION_MARKER_PATH)) {
|
if (existsSync(VERSION_MARKER_PATH)) {
|
||||||
return readFileSync(VERSION_MARKER_PATH, 'utf-8').trim();
|
const content = readFileSync(VERSION_MARKER_PATH, 'utf-8').trim();
|
||||||
|
|
||||||
|
// Try parsing as JSON (new format)
|
||||||
|
try {
|
||||||
|
const marker = JSON.parse(content);
|
||||||
|
return {
|
||||||
|
packageVersion: marker.packageVersion,
|
||||||
|
nodeVersion: marker.nodeVersion,
|
||||||
|
installedAt: marker.installedAt
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
// Fallback: old format (plain text version string)
|
||||||
|
return {
|
||||||
|
packageVersion: content,
|
||||||
|
nodeVersion: null, // Unknown
|
||||||
|
installedAt: null
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Marker doesn't exist or can't be read
|
// Marker doesn't exist or can't be read
|
||||||
@@ -62,9 +83,14 @@ function getInstalledVersion() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setInstalledVersion(version) {
|
function setInstalledVersion(packageVersion, nodeVersion) {
|
||||||
try {
|
try {
|
||||||
writeFileSync(VERSION_MARKER_PATH, version, 'utf-8');
|
const marker = {
|
||||||
|
packageVersion,
|
||||||
|
nodeVersion,
|
||||||
|
installedAt: new Date().toISOString()
|
||||||
|
};
|
||||||
|
writeFileSync(VERSION_MARKER_PATH, JSON.stringify(marker, null, 2), 'utf-8');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log(`⚠️ Failed to write version marker: ${error.message}`, colors.yellow);
|
log(`⚠️ Failed to write version marker: ${error.message}`, colors.yellow);
|
||||||
}
|
}
|
||||||
@@ -84,24 +110,77 @@ function needsInstall() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check version marker
|
// Check version marker
|
||||||
const currentVersion = getPackageVersion();
|
const currentPackageVersion = getPackageVersion();
|
||||||
const installedVersion = getInstalledVersion();
|
const currentNodeVersion = getNodeVersion();
|
||||||
|
const installed = getInstalledVersion();
|
||||||
|
|
||||||
if (!installedVersion) {
|
if (!installed) {
|
||||||
log('📦 No version marker found - installing', colors.cyan);
|
log('📦 No version marker found - installing', colors.cyan);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentVersion !== installedVersion) {
|
// Check package version
|
||||||
log(`📦 Version changed (${installedVersion} → ${currentVersion}) - updating`, colors.cyan);
|
if (currentPackageVersion !== installed.packageVersion) {
|
||||||
|
log(`📦 Version changed (${installed.packageVersion} → ${currentPackageVersion}) - updating`, colors.cyan);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Node.js version
|
||||||
|
if (installed.nodeVersion && currentNodeVersion !== installed.nodeVersion) {
|
||||||
|
log(`📦 Node.js version changed (${installed.nodeVersion} → ${currentNodeVersion}) - rebuilding native modules`, colors.cyan);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If old format (no nodeVersion), assume needs install
|
||||||
|
if (!installed.nodeVersion) {
|
||||||
|
log('📦 Old version marker format - updating', colors.cyan);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All good - no install needed
|
// All good - no install needed
|
||||||
log(`✓ Dependencies already installed (v${currentVersion})`, colors.dim);
|
log(`✓ Dependencies already installed (v${currentPackageVersion})`, colors.dim);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that better-sqlite3 native module loads correctly
|
||||||
|
* This catches ABI mismatches and corrupted builds
|
||||||
|
*/
|
||||||
|
async function verifyNativeModules() {
|
||||||
|
try {
|
||||||
|
log('🔍 Verifying native modules...', colors.dim);
|
||||||
|
|
||||||
|
// Try to actually load better-sqlite3
|
||||||
|
const { default: Database } = await import('better-sqlite3');
|
||||||
|
|
||||||
|
// Try to create a test in-memory database
|
||||||
|
const db = new Database(':memory:');
|
||||||
|
|
||||||
|
// Run a simple query to ensure it works
|
||||||
|
const result = db.prepare('SELECT 1 + 1 as result').get();
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
db.close();
|
||||||
|
|
||||||
|
if (result.result !== 2) {
|
||||||
|
throw new Error('SQLite math check failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
log('✓ Native modules verified', colors.dim);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 'ERR_DLOPEN_FAILED') {
|
||||||
|
log('⚠️ Native module ABI mismatch detected', colors.yellow);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other errors are unexpected - log and fail
|
||||||
|
log(`❌ Native module verification failed: ${error.message}`, colors.red);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getWindowsErrorHelp(errorOutput) {
|
function getWindowsErrorHelp(errorOutput) {
|
||||||
// Detect Python version at runtime
|
// Detect Python version at runtime
|
||||||
let pythonStatus = ' Python not detected or version unknown';
|
let pythonStatus = ' Python not detected or version unknown';
|
||||||
@@ -157,7 +236,7 @@ function getWindowsErrorHelp(errorOutput) {
|
|||||||
return help.join('\n');
|
return help.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
function runNpmInstall() {
|
async function runNpmInstall() {
|
||||||
const isWindows = process.platform === 'win32';
|
const isWindows = process.platform === 'win32';
|
||||||
|
|
||||||
log('', colors.cyan);
|
log('', colors.cyan);
|
||||||
@@ -175,7 +254,7 @@ function runNpmInstall() {
|
|||||||
for (const { command, label } of strategies) {
|
for (const { command, label } of strategies) {
|
||||||
try {
|
try {
|
||||||
log(`Attempting install ${label}...`, colors.dim);
|
log(`Attempting install ${label}...`, colors.dim);
|
||||||
|
|
||||||
// Run npm install silently
|
// Run npm install silently
|
||||||
execSync(command, {
|
execSync(command, {
|
||||||
cwd: PLUGIN_ROOT,
|
cwd: PLUGIN_ROOT,
|
||||||
@@ -188,12 +267,20 @@ function runNpmInstall() {
|
|||||||
throw new Error('better-sqlite3 installation verification failed');
|
throw new Error('better-sqlite3 installation verification failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
const version = getPackageVersion();
|
// NEW: Verify native modules actually work
|
||||||
setInstalledVersion(version);
|
const nativeModulesWork = await verifyNativeModules();
|
||||||
|
if (!nativeModulesWork) {
|
||||||
|
throw new Error('Native modules failed to load after install');
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageVersion = getPackageVersion();
|
||||||
|
const nodeVersion = getNodeVersion();
|
||||||
|
setInstalledVersion(packageVersion, nodeVersion);
|
||||||
|
|
||||||
log('', colors.green);
|
log('', colors.green);
|
||||||
log('✅ Dependencies installed successfully!', colors.bright);
|
log('✅ Dependencies installed successfully!', colors.bright);
|
||||||
log(` Version: ${version}`, colors.dim);
|
log(` Package version: ${packageVersion}`, colors.dim);
|
||||||
|
log(` Node.js version: ${nodeVersion}`, colors.dim);
|
||||||
log('', colors.reset);
|
log('', colors.reset);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -251,8 +338,8 @@ async function main() {
|
|||||||
const installNeeded = needsInstall();
|
const installNeeded = needsInstall();
|
||||||
|
|
||||||
if (installNeeded) {
|
if (installNeeded) {
|
||||||
// Run installation
|
// Run installation (now async)
|
||||||
const installSuccess = runNpmInstall();
|
const installSuccess = await runNpmInstall();
|
||||||
|
|
||||||
if (!installSuccess) {
|
if (!installSuccess) {
|
||||||
log('', colors.red);
|
log('', colors.red);
|
||||||
@@ -260,6 +347,22 @@ async function main() {
|
|||||||
log('', colors.reset);
|
log('', colors.reset);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// NEW: Even if install not needed, verify native modules work
|
||||||
|
const nativeModulesWork = await verifyNativeModules();
|
||||||
|
|
||||||
|
if (!nativeModulesWork) {
|
||||||
|
log('📦 Native modules need rebuild - reinstalling', colors.cyan);
|
||||||
|
const installSuccess = await runNpmInstall();
|
||||||
|
|
||||||
|
if (!installSuccess) {
|
||||||
|
log('', colors.red);
|
||||||
|
log('⚠️ Native module rebuild failed', colors.yellow);
|
||||||
|
log('', colors.reset);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try to start the PM2 worker after fresh install
|
// Try to start the PM2 worker after fresh install
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -5,10 +5,20 @@
|
|||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { homedir } from 'os';
|
import { homedir } from 'os';
|
||||||
import { existsSync, readFileSync } from 'fs';
|
import { existsSync, readFileSync, unlinkSync } from 'fs';
|
||||||
import { stdin } from 'process';
|
import { stdin } from 'process';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname } from 'path';
|
||||||
import { SessionStore } from '../services/sqlite/SessionStore.js';
|
import { SessionStore } from '../services/sqlite/SessionStore.js';
|
||||||
|
|
||||||
|
// Get __dirname equivalent in ESM
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
// Version marker path (same as smart-install.js)
|
||||||
|
// From src/hooks/ we need to go up to plugin root: ../../
|
||||||
|
const VERSION_MARKER_PATH = path.join(__dirname, '../../.install-version');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get context depth from settings
|
* Get context depth from settings
|
||||||
* Priority: ~/.claude/settings.json > env var > default
|
* Priority: ~/.claude/settings.json > env var > default
|
||||||
@@ -156,7 +166,27 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
|
|||||||
const cwd = input?.cwd ?? process.cwd();
|
const cwd = input?.cwd ?? process.cwd();
|
||||||
const project = cwd ? path.basename(cwd) : 'unknown-project';
|
const project = cwd ? path.basename(cwd) : 'unknown-project';
|
||||||
|
|
||||||
const db = new SessionStore();
|
let db: SessionStore;
|
||||||
|
try {
|
||||||
|
db = new SessionStore();
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.code === 'ERR_DLOPEN_FAILED') {
|
||||||
|
// Native module ABI mismatch - delete version marker to trigger reinstall
|
||||||
|
try {
|
||||||
|
unlinkSync(VERSION_MARKER_PATH);
|
||||||
|
} catch (unlinkError) {
|
||||||
|
// Marker might not exist, that's okay
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log once (not error spam) and exit cleanly
|
||||||
|
console.error('⚠️ Native module rebuild needed - restart Claude Code to auto-fix');
|
||||||
|
console.error(' (This happens after Node.js version upgrades)');
|
||||||
|
process.exit(0); // Exit cleanly to avoid error spam
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other errors should still throw
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
// Get ALL recent observations for this project (not filtered by summaries)
|
// Get ALL recent observations for this project (not filtered by summaries)
|
||||||
// This ensures we show observations even when summaries haven't been generated
|
// This ensures we show observations even when summaries haven't been generated
|
||||||
|
|||||||
+421
-21
@@ -297,10 +297,9 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.full-height-flex-layout {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
width: 100vw;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,8 +313,9 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 400px;
|
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
background: var(--color-bg-primary);
|
background: var(--color-bg-primary);
|
||||||
border-left: 1px solid var(--color-border-primary);
|
border-left: 1px solid var(--color-border-primary);
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -331,12 +331,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
padding: 14px 18px;
|
padding: 16px 24px;
|
||||||
border-bottom: 1px solid var(--color-border-primary);
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: var(--color-bg-header);
|
background: linear-gradient(to bottom,
|
||||||
|
var(--color-bg-header) 0%,
|
||||||
|
var(--color-bg-primary) 100%);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
@@ -354,13 +358,124 @@
|
|||||||
color: var(--color-text-header);
|
color: var(--color-text-header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-community-btn {
|
||||||
|
display: none;
|
||||||
|
background: var(--color-bg-card);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 14px;
|
||||||
|
height: 36px;
|
||||||
|
cursor: pointer;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
white-space: nowrap;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
margin: 16px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-community-btn:hover {
|
||||||
|
background: var(--color-bg-card-hover);
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-community-btn:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.sidebar-community-btn {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-project-filter {
|
||||||
|
display: none;
|
||||||
|
padding: 16px 18px;
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-project-filter label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-project-filter select {
|
||||||
|
width: 100%;
|
||||||
|
background: var(--color-bg-card);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 32px 0 12px;
|
||||||
|
height: 36px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%23666' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 10px center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-project-filter select:hover {
|
||||||
|
background-color: var(--color-bg-card-hover);
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-project-filter select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
box-shadow: 0 0 0 3px rgba(9, 105, 218, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.sidebar-project-filter {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-social-links {
|
||||||
|
display: none;
|
||||||
|
padding: 16px 18px;
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-social-links .icon-link {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.sidebar-social-links {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.header h1 {
|
.header h1 {
|
||||||
font-size: 16px;
|
font-size: 17px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--color-text-header);
|
color: var(--color-text-header);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 12px;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -385,7 +500,6 @@
|
|||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-family: 'Monaspace Radon', monospace;
|
font-family: 'Monaspace Radon', monospace;
|
||||||
min-width: 18px;
|
|
||||||
height: 18px;
|
height: 18px;
|
||||||
border-radius: 9px;
|
border-radius: 9px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -409,43 +523,106 @@
|
|||||||
.logo-text {
|
.logo-text {
|
||||||
font-family: 'Monaspace Radon', monospace;
|
font-family: 'Monaspace Radon', monospace;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
font-size: 20px;
|
font-size: 21px;
|
||||||
letter-spacing: -0.03em;
|
letter-spacing: -0.03em;
|
||||||
color: var(--color-text-logo);
|
color: var(--color-text-logo);
|
||||||
|
line-height: 1;
|
||||||
|
padding-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 8px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-btn,
|
.settings-btn,
|
||||||
.theme-toggle-btn {
|
.theme-toggle-btn {
|
||||||
background: transparent;
|
background: var(--color-bg-card);
|
||||||
border: 1px solid var(--color-border-primary);
|
border: 1px solid var(--color-border-primary);
|
||||||
padding: 8px;
|
border-radius: 6px;
|
||||||
|
padding: 0;
|
||||||
width: 36px;
|
width: 36px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-secondary);
|
||||||
transition: all 0.15s ease;
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-btn:hover,
|
.settings-btn:hover,
|
||||||
.theme-toggle-btn:hover {
|
.theme-toggle-btn:hover {
|
||||||
background: var(--color-bg-secondary);
|
background: var(--color-bg-card-hover);
|
||||||
border-color: var(--color-border-focus);
|
border-color: var(--color-border-focus);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-btn.active {
|
.settings-btn.active {
|
||||||
background: var(--color-bg-button);
|
background: linear-gradient(135deg, var(--color-bg-button) 0%, var(--color-accent-primary) 100%);
|
||||||
border-color: var(--color-bg-button);
|
border-color: var(--color-bg-button);
|
||||||
color: var(--color-text-button);
|
color: var(--color-text-button);
|
||||||
|
box-shadow: 0 2px 8px rgba(9, 105, 218, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-btn {
|
||||||
|
background: var(--color-bg-card);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 14px;
|
||||||
|
height: 36px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
white-space: nowrap;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-btn:hover {
|
||||||
|
background: var(--color-bg-card-hover);
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-btn:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
background: var(--color-bg-card);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-radius: 6px;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-link:hover {
|
||||||
|
background: var(--color-bg-card-hover);
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-icon,
|
.settings-icon,
|
||||||
@@ -492,6 +669,40 @@
|
|||||||
transition: all 0.15s ease;
|
transition: all 0.15s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status select {
|
||||||
|
background: var(--color-bg-card);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 32px 0 12px;
|
||||||
|
height: 36px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%23666' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 10px center;
|
||||||
|
max-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status select:hover {
|
||||||
|
background-color: var(--color-bg-card-hover);
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
box-shadow: 0 0 0 3px rgba(9, 105, 218, 0.1);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
select:hover,
|
select:hover,
|
||||||
input:hover {
|
input:hover {
|
||||||
border-color: var(--color-border-focus);
|
border-color: var(--color-border-focus);
|
||||||
@@ -527,15 +738,15 @@
|
|||||||
|
|
||||||
.feed {
|
.feed {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: scroll;
|
||||||
|
height: 100vh;
|
||||||
padding: 24px 18px;
|
padding: 24px 18px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.feed-content {
|
.feed-content {
|
||||||
width: 100%;
|
max-width: 650px;
|
||||||
max-width: 42rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
@@ -579,7 +790,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
min-width: 10%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-subheading-left {
|
.card-subheading-left {
|
||||||
@@ -1190,6 +1400,196 @@
|
|||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Utility: Container */
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Tablet Responsive Styles - 481px to 768px */
|
||||||
|
@media (max-width: 768px) and (min-width: 481px) {
|
||||||
|
/* Header stays on one line, hide icon links to save space */
|
||||||
|
.header {
|
||||||
|
padding: 14px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status select {
|
||||||
|
max-width: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide icon links (docs, github, twitter) on tablet */
|
||||||
|
.icon-link {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar full width on tablet */
|
||||||
|
.sidebar {
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Feed adjustments */
|
||||||
|
.feed {
|
||||||
|
padding: 20px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feed-content {
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card adjustments */
|
||||||
|
.card {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile & Small Tablet - 600px and below */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
/* Hide community button in header, will show in sidebar */
|
||||||
|
.community-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Responsive Styles - 480px and below */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
/* Hide project dropdown in header, will show in sidebar */
|
||||||
|
.status select {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header stays on one line */
|
||||||
|
.header {
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 15px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logomark {
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
scrollbar-width: none;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status select {
|
||||||
|
max-width: 140px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0 28px 0 10px;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide icon links on mobile */
|
||||||
|
.icon-link {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-btn,
|
||||||
|
.theme-toggle-btn,
|
||||||
|
.icon-link {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-btn {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.community-btn svg {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-icon,
|
||||||
|
.theme-toggle-btn svg,
|
||||||
|
.icon-link svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar adjustments for mobile */
|
||||||
|
.sidebar {
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-header {
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Feed adjustments */
|
||||||
|
.feed {
|
||||||
|
padding: 16px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Card adjustments */
|
||||||
|
.card {
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header-left {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stats grid to single column */
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form inputs full width */
|
||||||
|
.form-group input,
|
||||||
|
.form-group select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scroll to top button position */
|
||||||
|
.scroll-to-top {
|
||||||
|
bottom: 16px;
|
||||||
|
right: 16px;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|||||||
+28
-24
@@ -86,29 +86,30 @@ export function App() {
|
|||||||
}, [currentFilter]);
|
}, [currentFilter]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<>
|
||||||
<div className="main-col">
|
<Header
|
||||||
<Header
|
isConnected={isConnected}
|
||||||
isConnected={isConnected}
|
projects={projects}
|
||||||
projects={projects}
|
currentFilter={currentFilter}
|
||||||
currentFilter={currentFilter}
|
onFilterChange={setCurrentFilter}
|
||||||
onFilterChange={setCurrentFilter}
|
onSettingsToggle={toggleSidebar}
|
||||||
onSettingsToggle={toggleSidebar}
|
sidebarOpen={sidebarOpen}
|
||||||
sidebarOpen={sidebarOpen}
|
isProcessing={isProcessing}
|
||||||
isProcessing={isProcessing}
|
queueDepth={queueDepth}
|
||||||
queueDepth={queueDepth}
|
themePreference={preference}
|
||||||
themePreference={preference}
|
onThemeChange={setThemePreference}
|
||||||
onThemeChange={setThemePreference}
|
/>
|
||||||
/>
|
|
||||||
<Feed
|
|
||||||
observations={allObservations}
|
<Feed
|
||||||
summaries={allSummaries}
|
observations={allObservations}
|
||||||
prompts={allPrompts}
|
summaries={allSummaries}
|
||||||
onLoadMore={handleLoadMore}
|
prompts={allPrompts}
|
||||||
isLoading={pagination.observations.isLoading || pagination.summaries.isLoading || pagination.prompts.isLoading}
|
onLoadMore={handleLoadMore}
|
||||||
hasMore={pagination.observations.hasMore || pagination.summaries.hasMore || pagination.prompts.hasMore}
|
isLoading={pagination.observations.isLoading || pagination.summaries.isLoading || pagination.prompts.isLoading}
|
||||||
/>
|
hasMore={pagination.observations.hasMore || pagination.summaries.hasMore || pagination.prompts.hasMore}
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
|
|
||||||
<Sidebar
|
<Sidebar
|
||||||
isOpen={sidebarOpen}
|
isOpen={sidebarOpen}
|
||||||
@@ -117,10 +118,13 @@ export function App() {
|
|||||||
isSaving={isSaving}
|
isSaving={isSaving}
|
||||||
saveStatus={saveStatus}
|
saveStatus={saveStatus}
|
||||||
isConnected={isConnected}
|
isConnected={isConnected}
|
||||||
|
projects={projects}
|
||||||
|
currentFilter={currentFilter}
|
||||||
|
onFilterChange={setCurrentFilter}
|
||||||
onSave={saveSettings}
|
onSave={saveSettings}
|
||||||
onClose={toggleSidebar}
|
onClose={toggleSidebar}
|
||||||
onRefreshStats={refreshStats}
|
onRefreshStats={refreshStats}
|
||||||
/>
|
/>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,15 +46,7 @@ export function Header({
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
title="Documentation"
|
title="Documentation"
|
||||||
style={{
|
className="icon-link"
|
||||||
display: 'block',
|
|
||||||
padding: '8px 4px 8px 8px',
|
|
||||||
color: '#a0a0a0',
|
|
||||||
transition: 'color 0.2s',
|
|
||||||
lineHeight: 0
|
|
||||||
}}
|
|
||||||
onMouseEnter={(e) => e.currentTarget.style.color = '#606060'}
|
|
||||||
onMouseLeave={(e) => e.currentTarget.style.color = '#a0a0a0'}
|
|
||||||
>
|
>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path>
|
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path>
|
||||||
@@ -66,15 +58,7 @@ export function Header({
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
title="GitHub"
|
title="GitHub"
|
||||||
style={{
|
className="icon-link"
|
||||||
display: 'block',
|
|
||||||
padding: '8px 4px',
|
|
||||||
color: '#a0a0a0',
|
|
||||||
transition: 'color 0.2s',
|
|
||||||
lineHeight: 0
|
|
||||||
}}
|
|
||||||
onMouseEnter={(e) => e.currentTarget.style.color = '#606060'}
|
|
||||||
onMouseLeave={(e) => e.currentTarget.style.color = '#a0a0a0'}
|
|
||||||
>
|
>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
||||||
@@ -85,20 +69,24 @@ export function Header({
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
title="X (Twitter)"
|
title="X (Twitter)"
|
||||||
style={{
|
className="icon-link"
|
||||||
display: 'block',
|
|
||||||
padding: '8px 8px 8px 4px',
|
|
||||||
color: '#a0a0a0',
|
|
||||||
transition: 'color 0.2s',
|
|
||||||
lineHeight: 0
|
|
||||||
}}
|
|
||||||
onMouseEnter={(e) => e.currentTarget.style.color = '#606060'}
|
|
||||||
onMouseLeave={(e) => e.currentTarget.style.color = '#a0a0a0'}
|
|
||||||
>
|
>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://discord.gg/J4wttp9vDu"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="community-btn"
|
||||||
|
title="Join our Discord community"
|
||||||
|
>
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style={{ marginRight: '6px' }}>
|
||||||
|
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0a12.64 12.64 0 0 0-.617-1.25a.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.078.078 0 0 0 .084-.028a14.09 14.09 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13.107 13.107 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10.2 10.2 0 0 0 .372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127a12.299 12.299 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028a19.839 19.839 0 0 0 6.002-3.03a.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418z"/>
|
||||||
|
</svg>
|
||||||
|
<span>Community</span>
|
||||||
|
</a>
|
||||||
<select
|
<select
|
||||||
value={currentFilter}
|
value={currentFilter}
|
||||||
onChange={e => onFilterChange(e.target.value)}
|
onChange={e => onFilterChange(e.target.value)}
|
||||||
|
|||||||
@@ -10,12 +10,15 @@ interface SidebarProps {
|
|||||||
isSaving: boolean;
|
isSaving: boolean;
|
||||||
saveStatus: string;
|
saveStatus: string;
|
||||||
isConnected: boolean;
|
isConnected: boolean;
|
||||||
|
projects: string[];
|
||||||
|
currentFilter: string;
|
||||||
|
onFilterChange: (filter: string) => void;
|
||||||
onSave: (settings: Settings) => void;
|
onSave: (settings: Settings) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onRefreshStats: () => void;
|
onRefreshStats: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConnected, onSave, onClose, onRefreshStats }: SidebarProps) {
|
export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConnected, projects, currentFilter, onFilterChange, onSave, onClose, onRefreshStats }: SidebarProps) {
|
||||||
// Settings form state
|
// Settings form state
|
||||||
const [model, setModel] = useState(settings.CLAUDE_MEM_MODEL || DEFAULT_SETTINGS.CLAUDE_MEM_MODEL);
|
const [model, setModel] = useState(settings.CLAUDE_MEM_MODEL || DEFAULT_SETTINGS.CLAUDE_MEM_MODEL);
|
||||||
const [contextObs, setContextObs] = useState(settings.CLAUDE_MEM_CONTEXT_OBSERVATIONS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATIONS);
|
const [contextObs, setContextObs] = useState(settings.CLAUDE_MEM_CONTEXT_OBSERVATIONS || DEFAULT_SETTINGS.CLAUDE_MEM_CONTEXT_OBSERVATIONS);
|
||||||
@@ -26,19 +29,6 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
const [mcpToggling, setMcpToggling] = useState(false);
|
const [mcpToggling, setMcpToggling] = useState(false);
|
||||||
const [mcpStatus, setMcpStatus] = useState('');
|
const [mcpStatus, setMcpStatus] = useState('');
|
||||||
|
|
||||||
// Branch switching state
|
|
||||||
interface BranchInfo {
|
|
||||||
branch: string | null;
|
|
||||||
isBeta: boolean;
|
|
||||||
isGitRepo: boolean;
|
|
||||||
isDirty: boolean;
|
|
||||||
canSwitch: boolean;
|
|
||||||
error?: string;
|
|
||||||
}
|
|
||||||
const [branchInfo, setBranchInfo] = useState<BranchInfo | null>(null);
|
|
||||||
const [branchSwitching, setBranchSwitching] = useState(false);
|
|
||||||
const [branchStatus, setBranchStatus] = useState('');
|
|
||||||
|
|
||||||
// Update settings form state when settings change
|
// Update settings form state when settings change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setModel(settings.CLAUDE_MEM_MODEL || DEFAULT_SETTINGS.CLAUDE_MEM_MODEL);
|
setModel(settings.CLAUDE_MEM_MODEL || DEFAULT_SETTINGS.CLAUDE_MEM_MODEL);
|
||||||
@@ -54,14 +44,6 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
.catch(error => console.error('Failed to load MCP status:', error));
|
.catch(error => console.error('Failed to load MCP status:', error));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Fetch branch status on mount
|
|
||||||
useEffect(() => {
|
|
||||||
fetch('/api/branch/status')
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(data => setBranchInfo(data))
|
|
||||||
.catch(error => console.error('Failed to load branch status:', error));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Refresh stats when sidebar opens
|
// Refresh stats when sidebar opens
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
@@ -106,67 +88,6 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBranchSwitch = async (targetBranch: string) => {
|
|
||||||
setBranchSwitching(true);
|
|
||||||
setBranchStatus('Switching branches...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/branch/switch', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ branch: targetBranch })
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
setBranchStatus(`✓ ${result.message}`);
|
|
||||||
// Worker will restart, page will refresh
|
|
||||||
setTimeout(() => {
|
|
||||||
setBranchStatus('Restarting worker...');
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
setBranchStatus(`✗ Error: ${result.error}`);
|
|
||||||
setTimeout(() => setBranchStatus(''), 5000);
|
|
||||||
setBranchSwitching(false);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
setBranchStatus(`✗ Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
||||||
setTimeout(() => setBranchStatus(''), 5000);
|
|
||||||
setBranchSwitching(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBranchUpdate = async () => {
|
|
||||||
setBranchSwitching(true);
|
|
||||||
setBranchStatus('Checking for updates...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/branch/update', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' }
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
setBranchStatus(`✓ ${result.message}`);
|
|
||||||
// Worker will restart, page will refresh
|
|
||||||
setTimeout(() => {
|
|
||||||
setBranchStatus('Restarting worker...');
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
setBranchStatus(`✗ Error: ${result.error}`);
|
|
||||||
setTimeout(() => setBranchStatus(''), 5000);
|
|
||||||
setBranchSwitching(false);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
setBranchStatus(`✗ Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
||||||
setTimeout(() => setBranchStatus(''), 5000);
|
|
||||||
setBranchSwitching(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`sidebar ${isOpen ? 'open' : ''}`}>
|
<div className={`sidebar ${isOpen ? 'open' : ''}`}>
|
||||||
<div className="sidebar-header">
|
<div className="sidebar-header">
|
||||||
@@ -200,6 +121,67 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<a
|
||||||
|
href="https://discord.gg/J4wttp9vDu"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="sidebar-community-btn"
|
||||||
|
title="Join our Discord community"
|
||||||
|
>
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style={{ marginRight: '6px' }}>
|
||||||
|
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0a12.64 12.64 0 0 0-.617-1.25a.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.078.078 0 0 0 .084-.028a14.09 14.09 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13.107 13.107 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10.2 10.2 0 0 0 .372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127a12.299 12.299 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028a19.839 19.839 0 0 0 6.002-3.03a.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418z"/>
|
||||||
|
</svg>
|
||||||
|
<span>Community</span>
|
||||||
|
</a>
|
||||||
|
<div className="sidebar-social-links">
|
||||||
|
<a
|
||||||
|
href="https://docs.claude-mem.ai"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
title="Documentation"
|
||||||
|
className="icon-link"
|
||||||
|
>
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path>
|
||||||
|
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://github.com/thedotmack/claude-mem/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
title="GitHub"
|
||||||
|
className="icon-link"
|
||||||
|
>
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://x.com/Claude_Memory"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
title="X (Twitter)"
|
||||||
|
className="icon-link"
|
||||||
|
>
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="sidebar-project-filter">
|
||||||
|
<label htmlFor="sidebar-project-select">Filter by Project</label>
|
||||||
|
<select
|
||||||
|
id="sidebar-project-select"
|
||||||
|
value={currentFilter}
|
||||||
|
onChange={e => onFilterChange(e.target.value)}
|
||||||
|
>
|
||||||
|
<option value="">All Projects</option>
|
||||||
|
{projects.map(project => (
|
||||||
|
<option key={project} value={project}>{project}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div className="stats-scroll">
|
<div className="stats-scroll">
|
||||||
|
|
||||||
<div className="settings-section">
|
<div className="settings-section">
|
||||||
@@ -275,94 +257,6 @@ export function Sidebar({ isOpen, settings, stats, isSaving, saveStatus, isConne
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="settings-section">
|
|
||||||
<h3>Version Channel</h3>
|
|
||||||
<div className="form-group">
|
|
||||||
{branchInfo ? (
|
|
||||||
<>
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' }}>
|
|
||||||
<span style={{
|
|
||||||
padding: '2px 8px',
|
|
||||||
borderRadius: '4px',
|
|
||||||
fontSize: '11px',
|
|
||||||
fontWeight: 600,
|
|
||||||
background: branchInfo.isBeta ? '#6b4500' : '#1a4d1a',
|
|
||||||
color: branchInfo.isBeta ? '#ffb84d' : '#4ade80',
|
|
||||||
border: `1px solid ${branchInfo.isBeta ? '#ffb84d' : '#4ade80'}`
|
|
||||||
}}>
|
|
||||||
{branchInfo.isBeta ? 'Beta' : 'Stable'}
|
|
||||||
</span>
|
|
||||||
<span style={{ fontSize: '12px', opacity: 0.7 }}>
|
|
||||||
{branchInfo.branch || 'main'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{branchInfo.isBeta ? (
|
|
||||||
<>
|
|
||||||
<div className="setting-description" style={{ marginBottom: '12px' }}>
|
|
||||||
You're running the beta with Endless Mode. Your memory data is preserved when switching versions.
|
|
||||||
</div>
|
|
||||||
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
|
|
||||||
<button
|
|
||||||
onClick={() => handleBranchSwitch('main')}
|
|
||||||
disabled={branchSwitching}
|
|
||||||
style={{
|
|
||||||
background: '#2a2a2a',
|
|
||||||
border: '1px solid #404040',
|
|
||||||
padding: '8px 16px',
|
|
||||||
cursor: branchSwitching ? 'not-allowed' : 'pointer',
|
|
||||||
opacity: branchSwitching ? 0.5 : 1
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Switch to Stable
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={handleBranchUpdate}
|
|
||||||
disabled={branchSwitching}
|
|
||||||
style={{
|
|
||||||
background: '#2a2a2a',
|
|
||||||
border: '1px solid #404040',
|
|
||||||
padding: '8px 16px',
|
|
||||||
cursor: branchSwitching ? 'not-allowed' : 'pointer',
|
|
||||||
opacity: branchSwitching ? 0.5 : 1
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Check for Updates
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="setting-description" style={{ marginBottom: '12px' }}>
|
|
||||||
Try the beta to access experimental features like Endless Mode. Your memory data is preserved when switching.
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onClick={() => handleBranchSwitch('beta/7.0')}
|
|
||||||
disabled={branchSwitching}
|
|
||||||
style={{
|
|
||||||
background: '#4a3500',
|
|
||||||
border: '1px solid #ffb84d',
|
|
||||||
color: '#ffb84d',
|
|
||||||
padding: '8px 16px',
|
|
||||||
cursor: branchSwitching ? 'not-allowed' : 'pointer',
|
|
||||||
opacity: branchSwitching ? 0.5 : 1
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Try Beta (Endless Mode)
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{branchStatus && (
|
|
||||||
<div className="save-status" style={{ marginTop: '8px' }}>{branchStatus}</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div style={{ fontSize: '12px', opacity: 0.5 }}>Loading branch info...</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="settings-section">
|
<div className="settings-section">
|
||||||
<h3>Worker Stats</h3>
|
<h3>Worker Stats</h3>
|
||||||
<div className="stats-grid">
|
<div className="stats-grid">
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { test } from 'node:test';
|
||||||
|
import assert from 'node:assert';
|
||||||
|
import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
const VERSION_MARKER_PATH = join(process.cwd(), '.install-version');
|
||||||
|
|
||||||
|
test('version marker - new JSON format', () => {
|
||||||
|
const marker = {
|
||||||
|
packageVersion: '6.3.2',
|
||||||
|
nodeVersion: 'v22.21.1',
|
||||||
|
installedAt: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
writeFileSync(VERSION_MARKER_PATH, JSON.stringify(marker, null, 2));
|
||||||
|
const content = JSON.parse(readFileSync(VERSION_MARKER_PATH, 'utf-8'));
|
||||||
|
|
||||||
|
assert.strictEqual(content.packageVersion, '6.3.2');
|
||||||
|
assert.strictEqual(content.nodeVersion, 'v22.21.1');
|
||||||
|
assert.ok(content.installedAt);
|
||||||
|
|
||||||
|
unlinkSync(VERSION_MARKER_PATH);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('version marker - backward compatibility with old format', () => {
|
||||||
|
// Old format: plain text version string
|
||||||
|
writeFileSync(VERSION_MARKER_PATH, '6.3.2');
|
||||||
|
const content = readFileSync(VERSION_MARKER_PATH, 'utf-8').trim();
|
||||||
|
|
||||||
|
// Should be able to parse old format
|
||||||
|
let marker;
|
||||||
|
try {
|
||||||
|
marker = JSON.parse(content);
|
||||||
|
} catch {
|
||||||
|
// Old format - create compatible object
|
||||||
|
marker = {
|
||||||
|
packageVersion: content,
|
||||||
|
nodeVersion: null,
|
||||||
|
installedAt: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.strictEqual(marker.packageVersion, '6.3.2');
|
||||||
|
assert.strictEqual(marker.nodeVersion, null);
|
||||||
|
|
||||||
|
unlinkSync(VERSION_MARKER_PATH);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user