first commit
53
public/convert-icons.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Convert SVG Icons to PNG
|
||||
|
||||
I've created SVG versions of the app icons that match the MessageSquare design from the sidebar. To convert them to PNG format, you can use one of these methods:
|
||||
|
||||
## Method 1: Online Converter (Easiest)
|
||||
1. Go to https://cloudconvert.com/svg-to-png
|
||||
2. Upload each SVG file from the `/icons/` directory
|
||||
3. Download the PNG versions
|
||||
4. Replace the existing PNG files
|
||||
|
||||
## Method 2: Using Node.js (if you have it)
|
||||
```bash
|
||||
npm install sharp
|
||||
node -e "
|
||||
const sharp = require('sharp');
|
||||
const fs = require('fs');
|
||||
const sizes = [72, 96, 128, 144, 152, 192, 384, 512];
|
||||
sizes.forEach(size => {
|
||||
const svgPath = \`./icons/icon-\${size}x\${size}.svg\`;
|
||||
const pngPath = \`./icons/icon-\${size}x\${size}.png\`;
|
||||
if (fs.existsSync(svgPath)) {
|
||||
sharp(svgPath).png().toFile(pngPath);
|
||||
console.log(\`Converted \${svgPath} to \${pngPath}\`);
|
||||
}
|
||||
});
|
||||
"
|
||||
```
|
||||
|
||||
## Method 3: Using ImageMagick (if installed)
|
||||
```bash
|
||||
cd public/icons
|
||||
for size in 72 96 128 144 152 192 384 512; do
|
||||
convert "icon-${size}x${size}.svg" "icon-${size}x${size}.png"
|
||||
done
|
||||
```
|
||||
|
||||
## Method 4: Using Inkscape (if installed)
|
||||
```bash
|
||||
cd public/icons
|
||||
for size in 72 96 128 144 152 192 384 512; do
|
||||
inkscape --export-type=png "icon-${size}x${size}.svg"
|
||||
done
|
||||
```
|
||||
|
||||
## Icon Design
|
||||
The new icons feature:
|
||||
- Clean MessageSquare (chat bubble) design matching the sidebar
|
||||
- Primary color background with rounded corners
|
||||
- White stroke icon that's clearly visible
|
||||
- Consistent sizing and proportions across all sizes
|
||||
- Proper PWA-compliant format
|
||||
|
||||
Once converted, the PNG files will replace the existing ones and provide a consistent icon experience across all platforms.
|
||||
BIN
public/favicon.png
Normal file
|
After Width: | Height: | Size: 281 B |
9
public/favicon.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
|
||||
<!-- Background fills entire canvas -->
|
||||
<rect x="0" y="0" width="64" height="64" fill="hsl(240 5.9% 10%)"/>
|
||||
|
||||
<!-- MessageSquare icon - exact same as sidebar -->
|
||||
<g transform="translate(32, 32) scale(1.333) translate(-12, -12)" stroke="white" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 504 B |
49
public/generate-icons.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Icon sizes needed
|
||||
const sizes = [72, 96, 128, 144, 152, 192, 384, 512];
|
||||
|
||||
// SVG template function
|
||||
function createIconSVG(size) {
|
||||
const cornerRadius = Math.round(size * 0.25); // 25% corner radius
|
||||
const strokeWidth = Math.max(2, Math.round(size * 0.06)); // Scale stroke width
|
||||
|
||||
// MessageSquare path scaled to size
|
||||
const padding = Math.round(size * 0.25);
|
||||
const iconSize = size - (padding * 2);
|
||||
const startX = padding;
|
||||
const startY = Math.round(padding * 0.7);
|
||||
const endX = startX + iconSize;
|
||||
const endY = startY + Math.round(iconSize * 0.6);
|
||||
const tailX = startX;
|
||||
const tailY = endY + Math.round(iconSize * 0.3);
|
||||
|
||||
return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background with rounded corners -->
|
||||
<rect width="${size}" height="${size}" rx="${cornerRadius}" fill="hsl(262.1 83.3% 57.8%)"/>
|
||||
|
||||
<!-- MessageSquare icon -->
|
||||
<path d="M${startX} ${startY}C${startX} ${startY - 10} ${startX + 10} ${startY - 20} ${startX + 20} ${startY - 20}H${endX - 20}C${endX - 10} ${startY - 20} ${endX} ${startY - 10} ${endX} ${startY}V${endY - 20}C${endX} ${endY - 10} ${endX - 10} ${endY} ${endX - 20} ${endY}H${startX + Math.round(iconSize * 0.4)}L${tailX} ${tailY}V${startY}Z"
|
||||
stroke="white"
|
||||
stroke-width="${strokeWidth}"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"/>
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
// Generate SVG files for each size
|
||||
sizes.forEach(size => {
|
||||
const svgContent = createIconSVG(size);
|
||||
const filename = `icon-${size}x${size}.svg`;
|
||||
const filepath = path.join(__dirname, 'icons', filename);
|
||||
|
||||
fs.writeFileSync(filepath, svgContent);
|
||||
console.log(`Created ${filename}`);
|
||||
});
|
||||
|
||||
console.log('\nSVG icons created! To convert to PNG, you can use:');
|
||||
console.log('1. Online converter like cloudconvert.com');
|
||||
console.log('2. If you have ImageMagick: convert icon.svg icon.png');
|
||||
console.log('3. If you have Inkscape: inkscape --export-type=png icon.svg');
|
||||
19
public/icons/generate-icons.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# PWA Icons Required
|
||||
|
||||
Create the following icon files in this directory:
|
||||
|
||||
- icon-72x72.png
|
||||
- icon-96x96.png
|
||||
- icon-128x128.png
|
||||
- icon-144x144.png
|
||||
- icon-152x152.png
|
||||
- icon-192x192.png
|
||||
- icon-384x384.png
|
||||
- icon-512x512.png
|
||||
|
||||
You can use any icon generator tool or create them manually. The icons should be square and represent your Claude Code UI application.
|
||||
|
||||
For a quick solution, you can:
|
||||
1. Create a simple square PNG icon (512x512)
|
||||
2. Use online tools like realfavicongenerator.net to generate all sizes
|
||||
3. Or use ImageMagick: `convert icon-512x512.png -resize 192x192 icon-192x192.png`
|
||||
BIN
public/icons/icon-128x128.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
12
public/icons/icon-128x128.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background fills entire canvas - iOS will handle corner rounding -->
|
||||
<rect width="512" height="512" fill="hsl(240 5.9% 10%)"/>
|
||||
|
||||
<!-- MessageSquare icon - scaled and centered -->
|
||||
<path d="M128 144C128 126.327 142.327 112 160 112H352C369.673 112 384 126.327 384 144V272C384 289.673 369.673 304 352 304H224L128 400V144Z"
|
||||
stroke="white"
|
||||
stroke-width="32"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 578 B |
BIN
public/icons/icon-144x144.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
12
public/icons/icon-144x144.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background fills entire canvas - iOS will handle corner rounding -->
|
||||
<rect width="512" height="512" fill="hsl(240 5.9% 10%)"/>
|
||||
|
||||
<!-- MessageSquare icon - scaled and centered -->
|
||||
<path d="M128 144C128 126.327 142.327 112 160 112H352C369.673 112 384 126.327 384 144V272C384 289.673 369.673 304 352 304H224L128 400V144Z"
|
||||
stroke="white"
|
||||
stroke-width="32"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 578 B |
BIN
public/icons/icon-152x152.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
12
public/icons/icon-152x152.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background fills entire canvas - iOS will handle corner rounding -->
|
||||
<rect width="512" height="512" fill="hsl(240 5.9% 10%)"/>
|
||||
|
||||
<!-- MessageSquare icon - scaled and centered -->
|
||||
<path d="M128 144C128 126.327 142.327 112 160 112H352C369.673 112 384 126.327 384 144V272C384 289.673 369.673 304 352 304H224L128 400V144Z"
|
||||
stroke="white"
|
||||
stroke-width="32"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 578 B |
BIN
public/icons/icon-192x192.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
12
public/icons/icon-192x192.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background fills entire canvas - iOS will handle corner rounding -->
|
||||
<rect width="512" height="512" fill="hsl(240 5.9% 10%)"/>
|
||||
|
||||
<!-- MessageSquare icon - scaled and centered -->
|
||||
<path d="M128 144C128 126.327 142.327 112 160 112H352C369.673 112 384 126.327 384 144V272C384 289.673 369.673 304 352 304H224L128 400V144Z"
|
||||
stroke="white"
|
||||
stroke-width="32"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 578 B |
BIN
public/icons/icon-384x384.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
12
public/icons/icon-384x384.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background fills entire canvas - iOS will handle corner rounding -->
|
||||
<rect width="512" height="512" fill="hsl(240 5.9% 10%)"/>
|
||||
|
||||
<!-- MessageSquare icon - scaled and centered -->
|
||||
<path d="M128 144C128 126.327 142.327 112 160 112H352C369.673 112 384 126.327 384 144V272C384 289.673 369.673 304 352 304H224L128 400V144Z"
|
||||
stroke="white"
|
||||
stroke-width="32"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 578 B |
BIN
public/icons/icon-512x512.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
12
public/icons/icon-512x512.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background fills entire canvas - iOS will handle corner rounding -->
|
||||
<rect width="512" height="512" fill="hsl(240 5.9% 10%)"/>
|
||||
|
||||
<!-- MessageSquare icon - scaled and centered -->
|
||||
<path d="M128 144C128 126.327 142.327 112 160 112H352C369.673 112 384 126.327 384 144V272C384 289.673 369.673 304 352 304H224L128 400V144Z"
|
||||
stroke="white"
|
||||
stroke-width="32"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 578 B |
BIN
public/icons/icon-72x72.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
12
public/icons/icon-72x72.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background fills entire canvas - iOS will handle corner rounding -->
|
||||
<rect width="512" height="512" fill="hsl(240 5.9% 10%)"/>
|
||||
|
||||
<!-- MessageSquare icon - scaled and centered -->
|
||||
<path d="M128 144C128 126.327 142.327 112 160 112H352C369.673 112 384 126.327 384 144V272C384 289.673 369.673 304 352 304H224L128 400V144Z"
|
||||
stroke="white"
|
||||
stroke-width="32"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 578 B |
BIN
public/icons/icon-96x96.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
12
public/icons/icon-96x96.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background fills entire canvas - iOS will handle corner rounding -->
|
||||
<rect width="512" height="512" fill="hsl(240 5.9% 10%)"/>
|
||||
|
||||
<!-- MessageSquare icon - scaled and centered -->
|
||||
<path d="M128 144C128 126.327 142.327 112 160 112H352C369.673 112 384 126.327 384 144V272C384 289.673 369.673 304 352 304H224L128 400V144Z"
|
||||
stroke="white"
|
||||
stroke-width="32"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 578 B |
12
public/icons/icon-template.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Background fills entire canvas - iOS will handle corner rounding -->
|
||||
<rect width="512" height="512" fill="hsl(240 5.9% 10%)"/>
|
||||
|
||||
<!-- MessageSquare icon - scaled and centered -->
|
||||
<path d="M128 144C128 126.327 142.327 112 160 112H352C369.673 112 384 126.327 384 144V272C384 289.673 369.673 304 352 304H224L128 400V144Z"
|
||||
stroke="white"
|
||||
stroke-width="32"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 578 B |
9
public/logo.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="32" height="32" rx="8" fill="hsl(262.1 83.3% 57.8%)"/>
|
||||
<path d="M8 9C8 8.44772 8.44772 8 9 8H23C23.5523 8 24 8.44772 24 9V18C24 18.5523 23.5523 19 23 19H12L8 23V9Z"
|
||||
stroke="white"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 422 B |
61
public/manifest.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"name": "Claude Code UI",
|
||||
"short_name": "Claude UI",
|
||||
"description": "Claude Code UI web application",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#ffffff",
|
||||
"orientation": "portrait-primary",
|
||||
"scope": "/",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-128x128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-152x152.png",
|
||||
"sizes": "152x152",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
public/screenshots/desktop-main.png
Normal file
|
After Width: | Height: | Size: 385 KiB |
BIN
public/screenshots/mobile-chat.png
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
public/screenshots/tools-modal.png
Normal file
|
After Width: | Height: | Size: 295 KiB |
49
public/sw.js
Normal file
@@ -0,0 +1,49 @@
|
||||
// Service Worker for Claude Code UI PWA
|
||||
const CACHE_NAME = 'claude-ui-v1';
|
||||
const urlsToCache = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/manifest.json'
|
||||
];
|
||||
|
||||
// Install event
|
||||
self.addEventListener('install', event => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME)
|
||||
.then(cache => {
|
||||
return cache.addAll(urlsToCache);
|
||||
})
|
||||
);
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
// Fetch event
|
||||
self.addEventListener('fetch', event => {
|
||||
event.respondWith(
|
||||
caches.match(event.request)
|
||||
.then(response => {
|
||||
// Return cached response if found
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
// Otherwise fetch from network
|
||||
return fetch(event.request);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// Activate event
|
||||
self.addEventListener('activate', event => {
|
||||
event.waitUntil(
|
||||
caches.keys().then(cacheNames => {
|
||||
return Promise.all(
|
||||
cacheNames.map(cacheName => {
|
||||
if (cacheName !== CACHE_NAME) {
|
||||
return caches.delete(cacheName);
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||