Skip to content

Commit 7ec039c

Browse files
drakehanguyenDrakeNguyen
andauthored
Feature/UI clean up 2026 01 05 (#36)
* Update Support us and add items to mobile menu * Update version in the logo and update the release to include version * Add drop down menu for open tabs. * Add search command palette * Update search command palette. --------- Co-authored-by: DrakeNguyen <drake.ha.nguyen@gmail.com>
1 parent ac48707 commit 7ec039c

12 files changed

Lines changed: 753 additions & 72 deletions

File tree

.github/workflows/release.yml

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,8 @@ jobs:
125125
- name: Generate release notes from CHANGELOG
126126
id: release_notes
127127
run: |
128-
# Extract release notes from CHANGELOG.md for this version
129128
VERSION="${{ steps.tag_version.outputs.version }}"
130129
131-
# Try to extract changelog section for this version
132130
if [ -f "CHANGELOG.md" ]; then
133131
# Extract content between [version] and next version or end of file
134132
awk "/^## \[$VERSION\]/,/^## \[/ {if (!/^## \[$VERSION\]/ && !/^## \[/) print}" CHANGELOG.md > /tmp/release_notes.md || true
@@ -140,31 +138,30 @@ jobs:
140138
echo "See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for details." >> /tmp/release_notes.md
141139
fi
142140
143-
# Add header
144-
echo "## 🎉 Release $VERSION" > /tmp/release_body.md
145-
echo "" >> /tmp/release_body.md
146-
cat /tmp/release_notes.md >> /tmp/release_body.md
147-
echo "" >> /tmp/release_body.md
148-
echo "---" >> /tmp/release_body.md
149-
echo "" >> /tmp/release_body.md
150-
# Get the previous tag for comparison
151-
PREV_TAG=$(git describe --tags --abbrev=0 ${{ github.ref }}^ 2>/dev/null || echo "")
152-
if [ -n "$PREV_TAG" ]; then
153-
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/$PREV_TAG...${{ github.ref }}" >> /tmp/release_body.md
154-
else
155-
echo "**Full Changelog**: https://github.com/${{ github.repository }}/releases/tag/${{ github.ref }}" >> /tmp/release_body.md
156-
fi
141+
# Build release body
142+
{
143+
echo "## 🎉 Release $VERSION"
144+
echo ""
145+
cat /tmp/release_notes.md
146+
echo ""
147+
echo "---"
148+
echo ""
149+
# Get the previous tag for comparison
150+
PREV_TAG=$(git describe --tags --abbrev=0 "${{ steps.tag_version.outputs.tag_name }}^" 2>/dev/null || echo "")
151+
if [ -n "$PREV_TAG" ]; then
152+
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/$PREV_TAG...${{ steps.tag_version.outputs.tag_name }}"
153+
else
154+
echo "**Full Changelog**: https://github.com/${{ github.repository }}/releases/tag/${{ steps.tag_version.outputs.tag_name }}"
155+
fi
156+
} > /tmp/release_body.md
157157
else
158-
echo "## 🎉 Release $VERSION" > /tmp/release_body.md
159-
echo "" >> /tmp/release_body.md
160-
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ github.event.before }}...${{ github.ref }}" >> /tmp/release_body.md
158+
{
159+
echo "## 🎉 Release $VERSION"
160+
echo ""
161+
echo "**Full Changelog**: https://github.com/${{ github.repository }}/commits/${{ steps.tag_version.outputs.tag_name }}"
162+
} > /tmp/release_body.md
161163
fi
162164
163-
# Output for GitHub Actions
164-
echo "body<<EOF" >> $GITHUB_OUTPUT
165-
cat /tmp/release_body.md >> $GITHUB_OUTPUT
166-
echo "EOF" >> $GITHUB_OUTPUT
167-
168165
- name: Create GitHub Release
169166
uses: softprops/action-gh-release@v2
170167
with:
@@ -188,10 +185,46 @@ jobs:
188185
id: deployment
189186
uses: actions/deploy-pages@v4
190187

191-
- name: Upload build artifacts (optional)
188+
- name: Sync package.json version to main branch
189+
if: success()
190+
continue-on-error: true # Don't fail the workflow if this step fails
191+
run: |
192+
VERSION="${{ steps.tag_version.outputs.version }}"
193+
194+
# Configure git
195+
git config --local user.email "action@github.com"
196+
git config --local user.name "GitHub Action"
197+
198+
# Fetch main branch and check it out
199+
git fetch origin main
200+
git checkout main
201+
202+
# Check if package.json needs updating
203+
CURRENT_VERSION=$(node -p "require('./package.json').version")
204+
if [ "$CURRENT_VERSION" = "$VERSION" ]; then
205+
echo "✅ package.json already has version $VERSION, no commit needed"
206+
exit 0
207+
fi
208+
209+
# Update package.json version
210+
node -e "
211+
const fs = require('fs');
212+
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
213+
pkg.version = '$VERSION';
214+
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
215+
console.log('Updated package.json version to', '$VERSION');
216+
"
217+
218+
# Commit and push
219+
git add package.json
220+
git commit -m "chore: update version to $VERSION [skip ci]"
221+
git push origin main
222+
echo "✅ Committed and pushed updated package.json to main"
223+
224+
- name: Upload build artifacts
225+
if: success()
192226
uses: actions/upload-artifact@v4
193227
with:
194228
name: build-artifacts-${{ steps.tag_version.outputs.version }}
195229
path: out/
196230
retention-days: 30
197-

docs/RELEASES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Before creating a release, update:
4242
3. **next.config.js**: Update version in environment variables (if needed)
4343
```javascript
4444
env: {
45-
NEXT_PUBLIC_APP_VERSION: process.env.NEXT_PUBLIC_APP_VERSION || '0.1.0',
45+
NEXT_PUBLIC_APP_VERSION: process.env.NEXT_PUBLIC_APP_VERSION || 'dev',
4646
}
4747
```
4848

next.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ const nextConfig = {
1414
},
1515
env: {
1616
NEXT_PUBLIC_APP_NAME: process.env.NEXT_PUBLIC_APP_NAME || 'DevPockit',
17-
NEXT_PUBLIC_APP_VERSION: process.env.NEXT_PUBLIC_APP_VERSION || '0.1.0',
17+
// Version comes from GitHub release tag (v*) during CI/CD builds, shows 'dev' locally
18+
NEXT_PUBLIC_APP_VERSION: process.env.NEXT_PUBLIC_APP_VERSION || 'dev',
1819
},
1920
// HTTPS is handled via the dev:https script in package.json
2021
}

src/components/AppSidebar.tsx

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
Moon,
4848
PanelLeft,
4949
RefreshCw,
50+
Search,
5051
Settings,
5152
Sun,
5253
type LucideIcon,
@@ -76,6 +77,8 @@ interface AppSidebarProps extends React.ComponentProps<typeof Sidebar> {
7677
onHomeClick?: () => void
7778
onLogoClick?: () => void // Clears all state and navigates home
7879
onAboutClick?: () => void // Handles About page navigation
80+
onSearchClick?: () => void // Opens command palette
81+
onClearAllAndGoHome?: () => void // Clears all tabs and navigates home
7982
}
8083

8184
export function AppSidebar({
@@ -84,6 +87,8 @@ export function AppSidebar({
8487
onHomeClick,
8588
onLogoClick,
8689
onAboutClick,
90+
onSearchClick,
91+
onClearAllAndGoHome,
8792
...props
8893
}: AppSidebarProps) {
8994
const router = useRouter()
@@ -206,15 +211,39 @@ export function AppSidebar({
206211
className="flex-1 min-w-0 group-data-[collapsible=icon]:hidden cursor-pointer"
207212
onClick={handleLogoClick}
208213
>
209-
<div className="font-serif text-[24px] leading-[24px] tracking-normal text-sidebar-foreground hover:opacity-80 transition-opacity">
210-
DevPockit
214+
<div className="flex items-baseline gap-2 hover:opacity-80 transition-opacity">
215+
<span className="font-serif text-[24px] leading-[24px] tracking-normal text-sidebar-foreground">
216+
DevPockit
217+
</span>
218+
<span className="text-xs text-muted-foreground">
219+
v{process.env.NEXT_PUBLIC_APP_VERSION}
220+
</span>
211221
</div>
212222
</div>
213223
<SidebarTrigger className={cn("ml-auto", isCollapsed && "hidden")} />
214224
</div>
215225
<div className="mt-1 group-data-[collapsible=icon]:hidden">
216-
<SearchTools onToolSelect={handleToolSelect} />
226+
<SearchTools onToolSelect={handleToolSelect} onSearchClick={onSearchClick} />
217227
</div>
228+
{/* Search button for collapsed sidebar */}
229+
{isCollapsed && (
230+
<div className="mt-1">
231+
<Tooltip>
232+
<TooltipTrigger asChild>
233+
<button
234+
onClick={() => onSearchClick?.()}
235+
className="w-full h-9 flex items-center justify-center rounded-md hover:bg-sidebar-accent transition-colors"
236+
aria-label="Search tools"
237+
>
238+
<Search className="h-4 w-4 text-sidebar-foreground" />
239+
</button>
240+
</TooltipTrigger>
241+
<TooltipContent side="right">
242+
Search tools
243+
</TooltipContent>
244+
</Tooltip>
245+
</div>
246+
)}
218247
</SidebarHeader>
219248
<SidebarContent>
220249
<SidebarGroup className="mb-1">
@@ -224,7 +253,13 @@ export function AppSidebar({
224253
<SidebarMenuButton
225254
tooltip="All tools"
226255
isActive={pathname === '/'}
227-
onClick={handleHomeClick}
256+
onClick={() => {
257+
if (onClearAllAndGoHome) {
258+
onClearAllAndGoHome();
259+
} else {
260+
handleHomeClick();
261+
}
262+
}}
228263
>
229264
<Home className="h-4 w-4" />
230265
<span>All tools</span>
@@ -319,10 +354,12 @@ export function AppSidebar({
319354
</SidebarMenuButton>
320355
</SidebarMenuItem>
321356
<SidebarMenuItem>
322-
<SidebarMenuButton tooltip={isCollapsed ? "Support us" : undefined}>
323-
<Heart className="h-4 w-4" />
324-
<span className={cn(isCollapsed && "hidden")}>Support us</span>
325-
</SidebarMenuButton>
357+
<a href="https://buymeacoffee.com/hypkey" target="_blank" rel="noopener noreferrer">
358+
<SidebarMenuButton tooltip={isCollapsed ? "Support us" : undefined}>
359+
<Heart className="h-4 w-4" />
360+
<span className={cn(isCollapsed && "hidden")}>Support us</span>
361+
</SidebarMenuButton>
362+
</a>
326363
</SidebarMenuItem>
327364
</SidebarMenu>
328365

src/components/layout/AppLayout.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { startTransition, useCallback, useEffect, useRef, useState } from 'react
1212
import { AppSidebar } from '../AppSidebar';
1313
import { AboutPage } from '../pages/AboutPage';
1414
import { WelcomePage } from '../pages/WelcomePage';
15+
import { CommandPalette } from './CommandPalette';
1516
import { MobileTopBar } from './MobileTopBar';
1617
import { TopNavTabs, type ActiveTab } from './TopNavTabs';
1718

@@ -104,6 +105,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
104105
const [previousTool, setPreviousTool] = useState<string | undefined>();
105106
const [alertOpen, setAlertOpen] = useState(false);
106107
const [alertMessage, setAlertMessage] = useState('');
108+
const [isCommandPaletteOpen, setIsCommandPaletteOpen] = useState(false);
107109
const pathname = usePathname();
108110
const router = useRouter();
109111

@@ -252,30 +254,33 @@ function AppLayoutInner({ children }: AppLayoutProps) {
252254
return;
253255
}
254256

255-
// Escape to toggle between current tool and welcome page
257+
// Escape key handling - prioritize command palette if open
256258
if (e.key === 'Escape') {
259+
if (isCommandPaletteOpen) {
260+
// Command palette will handle its own close via onOpenChange
261+
return;
262+
}
257263
e.preventDefault();
258264
if (selectedTool && selectedInstanceId) {
259265
setPreviousTool(selectedTool);
260266
router.replace('/');
261267
} else {
262268
restorePreviousTool();
263269
}
270+
return;
264271
}
265272

266-
// Ctrl+K for search focus
267-
if (e.ctrlKey && e.key === 'k') {
273+
// Ctrl+K or Cmd+K for command palette
274+
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
268275
e.preventDefault();
269-
const searchInput = document.querySelector('input[placeholder*="Search"]') as HTMLInputElement;
270-
if (searchInput) {
271-
searchInput.focus();
272-
}
276+
setIsCommandPaletteOpen(true);
277+
return;
273278
}
274279
};
275280

276281
window.addEventListener('keydown', handleKeyDown);
277282
return () => window.removeEventListener('keydown', handleKeyDown);
278-
}, [selectedTool, selectedInstanceId, router, restorePreviousTool]);
283+
}, [selectedTool, selectedInstanceId, router, restorePreviousTool, isCommandPaletteOpen]);
279284

280285
const handleToolSelect = (toolId: string) => {
281286
const tool = getToolById(toolId);
@@ -406,6 +411,8 @@ function AppLayoutInner({ children }: AppLayoutProps) {
406411
onHomeClick={handleHomeClick}
407412
onLogoClick={handleClearAllAndGoHome}
408413
onAboutClick={handleAboutClick}
414+
onSearchClick={() => setIsCommandPaletteOpen(true)}
415+
onClearAllAndGoHome={handleClearAllAndGoHome}
409416
/>
410417
<SidebarInset>
411418
<div className={cn(
@@ -453,6 +460,13 @@ function AppLayoutInner({ children }: AppLayoutProps) {
453460
message={alertMessage}
454461
confirmText="OK"
455462
/>
463+
464+
{/* Command Palette */}
465+
<CommandPalette
466+
open={isCommandPaletteOpen}
467+
onOpenChange={setIsCommandPaletteOpen}
468+
onToolSelect={handleToolSelect}
469+
/>
456470
</>
457471
);
458472
}

0 commit comments

Comments
 (0)