add shelled-ui
This commit is contained in:
parent
c161af5f04
commit
701c6be0d3
5
.env
Normal file
5
.env
Normal file
@ -0,0 +1,5 @@
|
||||
git_pw_zia=f8e1300d871e905e27ce54c3455a3343104d9b04
|
||||
git_pw_master=817e2660d9f51dc5db0ac1bd06674f37154d8e14
|
||||
OLLAMA_API_KEY=a36032b17f894a2aa2a26575117a5347.Iok_WbBUSryMP1irX5ACv-3L
|
||||
OPEN_ROUTER_KEY=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
|
||||
OPEN_ROUTER_MODEL=nvidia/llama-nemotron-embed-vl-1b-v2:free
|
||||
6
shelled/package-lock.json
generated
Normal file
6
shelled/package-lock.json
generated
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "shelled",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
300
shelled/plan.md
Normal file
300
shelled/plan.md
Normal file
@ -0,0 +1,300 @@
|
||||
```markdown
|
||||
# Shelled OS UI Development Specification for LLM Implementation
|
||||
|
||||
## Project Overview
|
||||
Generate a complete Shelled OS UI implementation using HTML/CSS/JavaScript with Tauri 2.0 integration. The UI is a desktop environment that runs applications from Android, Windows, macOS, and Linux platforms.
|
||||
|
||||
## Technology Stack Requirements
|
||||
- Frontend: HTML5, CSS3, JavaScript ES6+ modules
|
||||
- Backend: Tauri 2.0 with Rust
|
||||
- Rendering: WebView
|
||||
- Icons: Google Material Icons
|
||||
- Fonts: Outfit (primary), Noto Sans SC (secondary)
|
||||
- Build System: Cargo (Rust) + npm
|
||||
|
||||
## Project Structure to Generate
|
||||
```
|
||||
shelled-os-ui/
|
||||
├── src/
|
||||
│ ├── index.html
|
||||
│ ├── css/
|
||||
│ │ ├── variables.css
|
||||
│ │ ├── main.css
|
||||
│ │ ├── taskbar.css
|
||||
│ │ ├── start-menu.css
|
||||
│ │ ├── file-explorer.css
|
||||
│ │ ├── browser.css
|
||||
│ │ └── settings.css
|
||||
│ ├── js/
|
||||
│ │ ├── main.js
|
||||
│ │ └── components/
|
||||
│ │ ├── Taskbar.js
|
||||
│ │ ├── StartMenu.js
|
||||
│ │ ├── FileExplorer.js
|
||||
│ │ ├── Browser.js
|
||||
│ │ └── Settings.js
|
||||
│ └── assets/
|
||||
│ └── images/
|
||||
│ └── conch-shell.png
|
||||
├── src-tauri/
|
||||
│ ├── src/
|
||||
│ │ └── main.rs
|
||||
│ ├── Cargo.toml
|
||||
│ └── tauri.conf.json
|
||||
└── package.json
|
||||
```
|
||||
|
||||
## HTML Structure Requirements
|
||||
|
||||
### Main Layout (index.html)
|
||||
- Full-screen desktop environment container
|
||||
- Gradient background (purple to blue gradient)
|
||||
- Bottom taskbar component
|
||||
- Popup windows for: Start Menu, File Explorer, Browser, Settings
|
||||
- Include Material Icons and Google Fonts CDN links
|
||||
|
||||
### Taskbar Component
|
||||
- Fixed at bottom of screen
|
||||
- Height: 60px
|
||||
- Background: Glassmorphism effect (semi-transparent white with blur)
|
||||
- Contains 4 clickable icons: Shell (start), Folder, Browser, Settings
|
||||
- System tray on right with WiFi, Volume, Battery icons
|
||||
- All icons must have hover effects and click handlers
|
||||
|
||||
### Start Menu Component
|
||||
- Position: Bottom-left, above taskbar
|
||||
- Size: 650px × 540px
|
||||
- Header with "All Applications" title
|
||||
- Search input box with real-time filtering
|
||||
- Two-column grid layout for apps
|
||||
- Alphabetically organized with letter dividers (A, B, C, D, G, M, W, Y)
|
||||
- Each app item contains: icon, name, description
|
||||
- Selection state with blue left border indicator
|
||||
|
||||
### File Explorer Component
|
||||
- Position: Bottom, offset from folder icon
|
||||
- Size: 580px × 480px
|
||||
- Header with "File Explorer" title and close button
|
||||
- List of mock files and folders
|
||||
- Click to select functionality
|
||||
|
||||
### Browser Component
|
||||
- Position: Bottom, offset from browser icon
|
||||
- Size: 520px × 420px
|
||||
- Header with "Browser" title and close button
|
||||
- Address bar with lock icon
|
||||
- Content area with placeholder
|
||||
|
||||
### Settings Component
|
||||
- Position: Bottom, offset from settings icon
|
||||
- Size: 460px × 380px
|
||||
- Header with "Settings" title and close button
|
||||
- List of toggle switches for: Dark Mode, Notifications, Sound Effects, Bluetooth, Location Services
|
||||
- Toggle switches must be clickable and change state
|
||||
|
||||
## CSS Requirements
|
||||
|
||||
### Design System Variables
|
||||
```css
|
||||
:root {
|
||||
--primary-blue: #0078D7;
|
||||
--secondary-blue: #005A9E;
|
||||
--accent-cyan: #00BCF2;
|
||||
--bg-white: #FFFFFF;
|
||||
--text-dark: #1F1F1F;
|
||||
--text-grey: #605E5C;
|
||||
--glass-bg: rgba(255, 255, 255, 0.15);
|
||||
--glass-border: rgba(255, 255, 255, 0.2);
|
||||
--shadow-lg: 0 -8px 32px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
```
|
||||
|
||||
### Animation Requirements
|
||||
- Popup windows: Slide up with scale animation (0.95 to 1.0)
|
||||
- Hover effects: scale(1.05) for buttons, translateX(4px) for list items
|
||||
- Transition timing: 0.2-0.3s ease
|
||||
- Use cubic-bezier(0.175, 0.885, 0.32, 1.275) for popup animations
|
||||
|
||||
### Glassmorphism Effects
|
||||
- Background: rgba(255, 255, 255, 0.15)
|
||||
- Backdrop-filter: blur(20px)
|
||||
- Border: 1px solid rgba(255, 255, 255, 0.2)
|
||||
|
||||
## JavaScript Requirements
|
||||
|
||||
### Main Controller (main.js)
|
||||
- Initialize all component controllers
|
||||
- Manage popup state (only one popup open at a time)
|
||||
- Click outside to close popups
|
||||
- Export application data (list of apps with icons, colors, descriptions)
|
||||
|
||||
### Component Controllers
|
||||
Each component should have:
|
||||
- `element` property referencing the DOM element
|
||||
- `buttonElement` property for the triggering button
|
||||
- `open()` method to show popup
|
||||
- `close()` method to hide popup
|
||||
- `render()` method to populate content
|
||||
- `initialize()` method to setup event listeners
|
||||
|
||||
### App Data Structure
|
||||
```javascript
|
||||
[
|
||||
{ name: "Angry Birds", icon: "mood_bad", color: "red", desc: "Mobile Game", letter: "A" },
|
||||
{ name: "Adobe Photoshop", icon: "edit", color: "green", desc: "Image Editor", letter: "A" },
|
||||
{ name: "Brave Browser", icon: "language", color: "blue", desc: "Web Browser", letter: "B" },
|
||||
{ name: "Books", icon: "book", color: "purple", desc: "Reading App", letter: "B" },
|
||||
{ name: "Chrome", icon: "code", color: "orange", desc: "Web Browser", letter: "C" },
|
||||
{ name: "Discord", icon: "chat", color: "blue", desc: "Communication", letter: "D" },
|
||||
{ name: "GarageBand", icon: "music_note", color: "yellow", desc: "macOS Music App", letter: "G" },
|
||||
{ name: "Movies & TV", icon: "movie", color: "purple", desc: "Media Player", letter: "M" },
|
||||
{ name: "Music", icon: "music_note", color: "green", desc: "Audio Player", letter: "M" },
|
||||
{ name: "Word", icon: "description", color: "default", desc: "Document Editor", letter: "W" },
|
||||
{ name: "YouTube", icon: "theaters", color: "orange", desc: "Video Platform", letter: "Y" }
|
||||
]
|
||||
```
|
||||
|
||||
### Search Functionality
|
||||
- Real-time filtering as user types
|
||||
- Filter by app name (case-insensitive)
|
||||
- Show/hide letter dividers based on visible apps
|
||||
- Clear search when popup closes
|
||||
|
||||
### File Explorer Data
|
||||
```javascript
|
||||
[
|
||||
{ name: "Documents", type: "folder", icon: "folder", meta: "5 items • Modified today" },
|
||||
{ name: "Downloads", type: "folder", icon: "folder", meta: "12 items • Modified yesterday" },
|
||||
{ name: "Pictures", type: "folder", icon: "folder", meta: "28 items • Modified 2 days ago" },
|
||||
{ name: "report.pdf", type: "file", icon: "insert_drive_file", meta: "PDF • 2.4 MB • Today" },
|
||||
{ name: "photo_001.jpg", type: "image", icon: "image", meta: "JPEG • 3.8 MB • Today" },
|
||||
{ name: "notes.txt", type: "document", icon: "description", meta: "Text • 1.2 KB • Yesterday" },
|
||||
{ name: "Music", type: "folder", icon: "folder", meta: "45 items • Modified last week" }
|
||||
]
|
||||
```
|
||||
|
||||
### Settings Data
|
||||
```javascript
|
||||
[
|
||||
{ label: "Dark Mode", icon: "dark_mode", active: false },
|
||||
{ label: "Notifications", icon: "notifications", active: true },
|
||||
{ label: "Sound Effects", icon: "volume_up", active: true },
|
||||
{ label: "Bluetooth", icon: "bluetooth", active: false },
|
||||
{ label: "Location Services", icon: "location_on", active: false }
|
||||
]
|
||||
```
|
||||
|
||||
## Tauri Configuration Requirements
|
||||
|
||||
### tauri.conf.json
|
||||
- Product name: "shelled-os"
|
||||
- Version: "0.1.0"
|
||||
- Window configuration: fullscreen, resizable
|
||||
- Identifier: "com.shelledos.app"
|
||||
- Enable all Tauri features for development
|
||||
|
||||
### Cargo.toml
|
||||
- Package name: "shelled-os-ui"
|
||||
- Tauri version: "2.0"
|
||||
- Dependencies: tauri, serde, serde_json
|
||||
- Edition: "2021"
|
||||
|
||||
### main.rs
|
||||
- Simple Tauri builder initialization
|
||||
- Windows subsystem configuration for release builds
|
||||
|
||||
## Interactive Behaviors
|
||||
|
||||
### Click Interactions
|
||||
1. Click Shell button → Toggle Start Menu
|
||||
2. Click Folder button → Toggle File Explorer
|
||||
3. Click Browser button → Toggle Browser popup
|
||||
4. Click Settings button → Toggle Settings popup
|
||||
5. Click app item → Select it (add blue left border)
|
||||
6. Click file item → Select it
|
||||
7. Click toggle switch → Toggle active state
|
||||
8. Click close button (X) → Close respective popup
|
||||
9. Click outside popup → Close all popups
|
||||
|
||||
### Search Interactions
|
||||
1. Type in search box → Filter apps in real-time
|
||||
2. Empty search → Show all apps
|
||||
3. Partial match → Show matching apps
|
||||
4. No match → Show no apps (hide letter dividers)
|
||||
|
||||
### Visual Feedback
|
||||
1. Hover over button → Scale up (1.05)
|
||||
2. Hover over list item → Slide right (4px) and change color
|
||||
3. Active popup → Button shows indicator
|
||||
4. Selected item → Blue left border appears
|
||||
5. Toggle switch → Smooth transition of thumb position
|
||||
|
||||
## Color Coding for App Icons
|
||||
- red: linear-gradient(135deg, #e52e00 0%, #ff6b35 100%)
|
||||
- green: linear-gradient(135deg, #11998e 0%, #38ef7d 100%)
|
||||
- blue: linear-gradient(135deg, #0078D7 0%, #00BCF2 100%)
|
||||
- orange: linear-gradient(135deg, #f093fb 0%, #f5576c 100%)
|
||||
- purple: linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%)
|
||||
- yellow: linear-gradient(135deg, #f7971e 0%, #ffd200 100%)
|
||||
- default: linear-gradient(135deg, #667eea 0%, #764ba2 100%)
|
||||
|
||||
## File Icon Colors
|
||||
- folder: #ff9800
|
||||
- file: #90caf9
|
||||
- image: #66bb6a
|
||||
- document: #42a5f5
|
||||
|
||||
## Performance Requirements
|
||||
- Startup time: < 3 seconds
|
||||
- Menu open animation: < 200ms
|
||||
- Search filtering: < 50ms response time
|
||||
- All animations: 60fps
|
||||
- Memory usage: < 150MB
|
||||
|
||||
## Browser Compatibility
|
||||
- Chrome 90+
|
||||
- Firefox 88+
|
||||
- Safari 14+
|
||||
- Edge 90+
|
||||
|
||||
## Testing Checklist
|
||||
After implementation, verify:
|
||||
- [ ] Application opens fullscreen
|
||||
- [ ] Taskbar displays at bottom
|
||||
- [ ] All 4 icons are clickable
|
||||
- [ ] Clicking Shell opens Start Menu
|
||||
- [ ] Start Menu shows 11 apps alphabetically
|
||||
- [ ] Search filters apps correctly
|
||||
- [ ] Clicking apps selects them
|
||||
- [ ] File Explorer opens with mock files
|
||||
- [ ] Browser popup opens
|
||||
- [ ] Settings popup opens with working toggles
|
||||
- [ ] Close buttons work
|
||||
- [ ] Click outside closes popups
|
||||
- [ ] Animations are smooth
|
||||
- [ ] No console errors
|
||||
|
||||
## Implementation Notes
|
||||
- Use ES6 modules for JavaScript
|
||||
- All event listeners must use stopPropagation where appropriate
|
||||
- Use document fragments for DOM manipulation
|
||||
- Implement proper cleanup when closing popups
|
||||
- Use CSS transitions, not JavaScript animations where possible
|
||||
- Ensure z-index management prevents layering issues
|
||||
- All text must use the Outfit font family
|
||||
|
||||
## Critical Constraints
|
||||
1. DO NOT use any external JavaScript libraries
|
||||
2. DO NOT modify the specified color values
|
||||
3. DO NOT change the layout dimensions
|
||||
4. DO NOT alter the interaction behaviors
|
||||
5. DO NOT add additional features beyond specification
|
||||
6. MUST maintain exact animations as specified
|
||||
7. MUST use glassmorphism effects as described
|
||||
8. MUST implement all search functionality
|
||||
9. MUST ensure all toggles are interactive
|
||||
10. MUST follow the exact component architecture
|
||||
|
||||
---
|
||||
End of Specification Document
|
||||
```
|
||||
2
shelled/shelled-os-ui/.gitignore
vendored
Normal file
2
shelled/shelled-os-ui/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
dist/
|
||||
244
shelled/shelled-os-ui/package-lock.json
generated
Normal file
244
shelled/shelled-os-ui/package-lock.json
generated
Normal file
@ -0,0 +1,244 @@
|
||||
{
|
||||
"name": "shelled-os-ui",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "shelled-os-ui",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@tauri-apps/api": "^2.0.0-rc",
|
||||
"@tauri-apps/cli": "^2.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/api": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz",
|
||||
"integrity": "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/tauri"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.10.1.tgz",
|
||||
"integrity": "sha512-jQNGF/5quwORdZSSLtTluyKQ+o6SMa/AUICfhf4egCGFdMHqWssApVgYSbg+jmrZoc8e1DscNvjTnXtlHLS11g==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"bin": {
|
||||
"tauri": "tauri.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/tauri"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tauri-apps/cli-darwin-arm64": "2.10.1",
|
||||
"@tauri-apps/cli-darwin-x64": "2.10.1",
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf": "2.10.1",
|
||||
"@tauri-apps/cli-linux-arm64-gnu": "2.10.1",
|
||||
"@tauri-apps/cli-linux-arm64-musl": "2.10.1",
|
||||
"@tauri-apps/cli-linux-riscv64-gnu": "2.10.1",
|
||||
"@tauri-apps/cli-linux-x64-gnu": "2.10.1",
|
||||
"@tauri-apps/cli-linux-x64-musl": "2.10.1",
|
||||
"@tauri-apps/cli-win32-arm64-msvc": "2.10.1",
|
||||
"@tauri-apps/cli-win32-ia32-msvc": "2.10.1",
|
||||
"@tauri-apps/cli-win32-x64-msvc": "2.10.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-darwin-arm64": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.10.1.tgz",
|
||||
"integrity": "sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-darwin-x64": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.10.1.tgz",
|
||||
"integrity": "sha512-V/irQVvjPMGOTQqNj55PnQPVuH4VJP8vZCN7ajnj+ZS8Kom1tEM2hR3qbbIRoS3dBKs5mbG8yg1WC+97dq17Pw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.10.1.tgz",
|
||||
"integrity": "sha512-Hyzwsb4VnCWKGfTw+wSt15Z2pLw2f0JdFBfq2vHBOBhvg7oi6uhKiF87hmbXOBXUZaGkyRDkCHsdzJcIfoJC2w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-arm64-gnu": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.10.1.tgz",
|
||||
"integrity": "sha512-OyOYs2t5GkBIvyWjA1+h4CZxTcdz1OZPCWAPz5DYEfB0cnWHERTnQ/SLayQzncrT0kwRoSfSz9KxenkyJoTelA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-arm64-musl": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.10.1.tgz",
|
||||
"integrity": "sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-riscv64-gnu": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.10.1.tgz",
|
||||
"integrity": "sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-x64-gnu": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.10.1.tgz",
|
||||
"integrity": "sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-x64-musl": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.10.1.tgz",
|
||||
"integrity": "sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-win32-arm64-msvc": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.10.1.tgz",
|
||||
"integrity": "sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-win32-ia32-msvc": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.10.1.tgz",
|
||||
"integrity": "sha512-gXyxgEzsFegmnWywYU5pEBURkcFN/Oo45EAwvZrHMh+zUSEAvO5E8TXsgPADYm31d1u7OQU3O3HsYfVBf2moHw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-win32-x64-msvc": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.10.1.tgz",
|
||||
"integrity": "sha512-6Cn7YpPFwzChy0ERz6djKEmUehWrYlM+xTaNzGPgZocw3BD7OfwfWHKVWxXzdjEW2KfKkHddfdxK1XXTYqBRLg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
shelled/shelled-os-ui/package.json
Normal file
15
shelled/shelled-os-ui/package.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "shelled-os-ui",
|
||||
"version": "1.0.0",
|
||||
"description": "Shelled OS UI for Tauri",
|
||||
"main": "src/js/main.js",
|
||||
"scripts": {
|
||||
"dev": "tauri dev",
|
||||
"build": "tauri build"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^2.0.0-rc",
|
||||
"@tauri-apps/api": "^2.0.0-rc"
|
||||
}
|
||||
}
|
||||
14
shelled/shelled-os-ui/src-tauri/Cargo.toml
Normal file
14
shelled/shelled-os-ui/src-tauri/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "shelled-os-ui"
|
||||
version = "0.1.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.0.0-rc", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2.0.0-rc", features = [] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
10
shelled/shelled-os-ui/src-tauri/src/main.rs
Normal file
10
shelled/shelled-os-ui/src-tauri/src/main.rs
Normal file
@ -0,0 +1,10 @@
|
||||
#![cfg_attr(
|
||||
all(not(debug_assertions), target_os = "windows"),
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
25
shelled/shelled-os-ui/src-tauri/tauri.conf.json
Normal file
25
shelled/shelled-os-ui/src-tauri/tauri.conf.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"productName": "shelled-os",
|
||||
"version": "0.1.0",
|
||||
"identifier": "com.shelledos.app",
|
||||
"build": {
|
||||
"beforeDevCommand": "",
|
||||
"beforeBuildCommand": "",
|
||||
"devPath": "../src",
|
||||
"distDir": "../src"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "Shelled OS",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"fullscreen": true,
|
||||
"resizable": true
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
}
|
||||
}
|
||||
52
shelled/shelled-os-ui/src/css/browser.css
Normal file
52
shelled/shelled-os-ui/src/css/browser.css
Normal file
@ -0,0 +1,52 @@
|
||||
#browser {
|
||||
left: 140px; /* Offset from browser icon */
|
||||
width: 520px;
|
||||
height: 420px;
|
||||
z-index: 80;
|
||||
}
|
||||
|
||||
.address-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 16px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
.lock-icon {
|
||||
font-size: 16px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.address-bar input {
|
||||
flex-grow: 1;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--bg-white);
|
||||
font-family: inherit;
|
||||
font-size: 0.9rem;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.browser-content {
|
||||
flex-grow: 1;
|
||||
background: var(--bg-white);
|
||||
color: var(--text-dark);
|
||||
padding: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.browser-content h1 {
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
color: var(--primary-blue);
|
||||
}
|
||||
|
||||
.browser-content p {
|
||||
color: var(--text-grey);
|
||||
}
|
||||
62
shelled/shelled-os-ui/src/css/file-explorer.css
Normal file
62
shelled/shelled-os-ui/src/css/file-explorer.css
Normal file
@ -0,0 +1,62 @@
|
||||
#file-explorer {
|
||||
left: 80px; /* Offset from folder icon */
|
||||
width: 580px;
|
||||
height: 480px;
|
||||
z-index: 85;
|
||||
}
|
||||
|
||||
.file-list {
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, background 0.2s ease;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.file-item:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.file-item.selected {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-left: 3px solid var(--accent-cyan);
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
margin-right: 16px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.file-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-weight: 500;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.file-meta {
|
||||
font-size: 0.8rem;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.file-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.file-list::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
94
shelled/shelled-os-ui/src/css/main.css
Normal file
94
shelled/shelled-os-ui/src/css/main.css
Normal file
@ -0,0 +1,94 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Outfit', sans-serif;
|
||||
overflow: hidden;
|
||||
color: var(--text-dark);
|
||||
}
|
||||
|
||||
#desktop {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: linear-gradient(135deg, #4b1a8a 0%, #1e3c72 100%);
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#popups-container {
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
pointer-events: none; /* Let clicks pass through desktop */
|
||||
}
|
||||
|
||||
/* Glassmorphism utility */
|
||||
.glass {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid var(--glass-border);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
/* Base Popup Styles */
|
||||
.popup {
|
||||
position: absolute;
|
||||
bottom: 70px;
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--shadow-lg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
transition: opacity 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275), transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
visibility: hidden;
|
||||
pointer-events: auto; /* Re-enable pointer events for popups */
|
||||
color: var(--bg-white);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.popup.active {
|
||||
opacity: 1;
|
||||
transform: scale(1.0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.popup-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
.popup-header h2 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--bg-white);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px;
|
||||
border-radius: 50%;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
71
shelled/shelled-os-ui/src/css/settings.css
Normal file
71
shelled/shelled-os-ui/src/css/settings.css
Normal file
@ -0,0 +1,71 @@
|
||||
#settings {
|
||||
left: 200px; /* Offset from settings icon */
|
||||
width: 460px;
|
||||
height: 380px;
|
||||
z-index: 75;
|
||||
}
|
||||
|
||||
.settings-list {
|
||||
flex-grow: 1;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.setting-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.setting-icon {
|
||||
margin-right: 12px;
|
||||
font-size: 24px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.setting-label {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Toggle Switch Styles */
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.toggle-switch.active {
|
||||
background: var(--primary-blue);
|
||||
}
|
||||
|
||||
.toggle-thumb {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: var(--bg-white);
|
||||
border-radius: 50%;
|
||||
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.toggle-switch.active .toggle-thumb {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
115
shelled/shelled-os-ui/src/css/start-menu.css
Normal file
115
shelled/shelled-os-ui/src/css/start-menu.css
Normal file
@ -0,0 +1,115 @@
|
||||
#start-menu {
|
||||
left: 20px;
|
||||
width: 650px;
|
||||
height: 540px;
|
||||
z-index: 90;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
padding: 16px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
#app-search {
|
||||
flex-grow: 1;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--bg-white);
|
||||
font-size: 1rem;
|
||||
font-family: 'Outfit', sans-serif;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#app-search::placeholder {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.app-grid {
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
/* Custom Scrollbar for App Grid */
|
||||
.app-grid::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.app-grid::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.letter-divider {
|
||||
grid-column: 1 / -1;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 4px;
|
||||
margin-bottom: 4px;
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.app-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, background 0.2s ease, border-left 0.2s ease;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.app-item:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.app-item.selected {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-left: 3px solid var(--accent-cyan);
|
||||
}
|
||||
|
||||
.app-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 16px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.app-icon .material-icons {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.app-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.app-name {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.app-desc {
|
||||
font-size: 0.8rem;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
76
shelled/shelled-os-ui/src/css/taskbar.css
Normal file
76
shelled/shelled-os-ui/src/css/taskbar.css
Normal file
@ -0,0 +1,76 @@
|
||||
#taskbar {
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-top: 1px solid var(--glass-border);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.taskbar-icons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.taskbar-btn {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 8px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--bg-white);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, background 0.2s ease;
|
||||
}
|
||||
|
||||
.taskbar-btn img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.taskbar-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.taskbar-btn.active {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.taskbar-btn.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
width: 16px;
|
||||
height: 3px;
|
||||
background: var(--bg-white);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.system-tray {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
color: var(--bg-white);
|
||||
}
|
||||
|
||||
.tray-icon {
|
||||
font-size: 18px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.clock {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
26
shelled/shelled-os-ui/src/css/variables.css
Normal file
26
shelled/shelled-os-ui/src/css/variables.css
Normal file
@ -0,0 +1,26 @@
|
||||
:root {
|
||||
--primary-blue: #0078D7;
|
||||
--secondary-blue: #005A9E;
|
||||
--accent-cyan: #00BCF2;
|
||||
--bg-white: #FFFFFF;
|
||||
--text-dark: #1F1F1F;
|
||||
--text-grey: #605E5C;
|
||||
--glass-bg: rgba(255, 255, 255, 0.15);
|
||||
--glass-border: rgba(255, 255, 255, 0.2);
|
||||
--shadow-lg: 0 -8px 32px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* Color properties for app icons */
|
||||
.color-red { background: linear-gradient(135deg, #e52e00 0%, #ff6b35 100%); }
|
||||
.color-green { background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); }
|
||||
.color-blue { background: linear-gradient(135deg, #0078D7 0%, #00BCF2 100%); }
|
||||
.color-orange { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
|
||||
.color-purple { background: linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%); }
|
||||
.color-yellow { background: linear-gradient(135deg, #f7971e 0%, #ffd200 100%); }
|
||||
.color-default { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
|
||||
|
||||
/* File colors */
|
||||
.file-folder { color: #ff9800; }
|
||||
.file-file { color: #90caf9; }
|
||||
.file-image { color: #66bb6a; }
|
||||
.file-document { color: #42a5f5; }
|
||||
101
shelled/shelled-os-ui/src/index.html
Normal file
101
shelled/shelled-os-ui/src/index.html
Normal file
@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Shelled OS</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&family=Noto+Sans+SC:wght@300;400;500&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<link rel="stylesheet" href="./css/variables.css">
|
||||
<link rel="stylesheet" href="./css/main.css">
|
||||
<link rel="stylesheet" href="./css/taskbar.css">
|
||||
<link rel="stylesheet" href="./css/start-menu.css">
|
||||
<link rel="stylesheet" href="./css/file-explorer.css">
|
||||
<link rel="stylesheet" href="./css/browser.css">
|
||||
<link rel="stylesheet" href="./css/settings.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="desktop">
|
||||
<!-- Popups Container -->
|
||||
<div id="popups-container">
|
||||
<!-- Start Menu Popup -->
|
||||
<div id="start-menu" class="popup">
|
||||
<div class="popup-header">
|
||||
<h2>All Applications</h2>
|
||||
</div>
|
||||
<div class="search-container">
|
||||
<span class="material-icons search-icon">search</span>
|
||||
<input type="text" id="app-search" placeholder="Type to search...">
|
||||
</div>
|
||||
<div id="app-grid" class="app-grid">
|
||||
<!-- Apps dynamically added here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File Explorer Popup -->
|
||||
<div id="file-explorer" class="popup">
|
||||
<div class="popup-header">
|
||||
<h2>File Explorer</h2>
|
||||
<button class="close-btn"><span class="material-icons">close</span></button>
|
||||
</div>
|
||||
<div class="file-list" id="file-list">
|
||||
<!-- Files dynamically added here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Browser Popup -->
|
||||
<div id="browser" class="popup">
|
||||
<div class="popup-header">
|
||||
<h2>Browser</h2>
|
||||
<button class="close-btn"><span class="material-icons">close</span></button>
|
||||
</div>
|
||||
<div class="address-bar">
|
||||
<span class="material-icons lock-icon">lock</span>
|
||||
<input type="text" value="https://example.com" readonly>
|
||||
</div>
|
||||
<div class="browser-content">
|
||||
<h1>Welcome to the Web</h1>
|
||||
<p>This is a placeholder for browser content.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Popup -->
|
||||
<div id="settings" class="popup">
|
||||
<div class="popup-header">
|
||||
<h2>Settings</h2>
|
||||
<button class="close-btn"><span class="material-icons">close</span></button>
|
||||
</div>
|
||||
<div class="settings-list" id="settings-list">
|
||||
<!-- Settings dynamically added here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Taskbar -->
|
||||
<div id="taskbar">
|
||||
<div class="taskbar-icons">
|
||||
<button class="taskbar-btn" id="btn-start" title="Start">
|
||||
<img src="./assets/images/conch-shell.png" alt="Shell" onerror="this.src=''; this.className='material-icons'; this.innerHTML='widgets'; this.alt='Start'">
|
||||
</button>
|
||||
<button class="taskbar-btn" id="btn-explorer" title="File Explorer">
|
||||
<span class="material-icons">folder</span>
|
||||
</button>
|
||||
<button class="taskbar-btn" id="btn-browser" title="Browser">
|
||||
<span class="material-icons">language</span>
|
||||
</button>
|
||||
<button class="taskbar-btn" id="btn-settings" title="Settings">
|
||||
<span class="material-icons">settings</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="system-tray">
|
||||
<span class="material-icons tray-icon">wifi</span>
|
||||
<span class="material-icons tray-icon">volume_up</span>
|
||||
<span class="material-icons tray-icon">battery_full</span>
|
||||
<span class="clock" id="clock">12:00 PM</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module" src="./js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
26
shelled/shelled-os-ui/src/js/components/Browser.js
Normal file
26
shelled/shelled-os-ui/src/js/components/Browser.js
Normal file
@ -0,0 +1,26 @@
|
||||
export default class Browser {
|
||||
constructor(os) {
|
||||
this.os = os;
|
||||
this.element = document.getElementById('browser');
|
||||
this.closeBtn = this.element.querySelector('.close-btn');
|
||||
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.closeBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
this.os.closeAllPopups();
|
||||
});
|
||||
}
|
||||
|
||||
open() {
|
||||
this.element.classList.add('active');
|
||||
this.os.components.taskbar.updateActiveButton('browser');
|
||||
}
|
||||
|
||||
close() {
|
||||
this.element.classList.remove('active');
|
||||
this.os.components.taskbar.updateActiveButton(null);
|
||||
}
|
||||
}
|
||||
68
shelled/shelled-os-ui/src/js/components/FileExplorer.js
Normal file
68
shelled/shelled-os-ui/src/js/components/FileExplorer.js
Normal file
@ -0,0 +1,68 @@
|
||||
export default class FileExplorer {
|
||||
constructor(os) {
|
||||
this.os = os;
|
||||
this.element = document.getElementById('file-explorer');
|
||||
this.listElement = document.getElementById('file-list');
|
||||
this.closeBtn = this.element.querySelector('.close-btn');
|
||||
|
||||
this.fileData = [
|
||||
{ name: "Documents", type: "folder", icon: "folder", meta: "5 items • Modified today" },
|
||||
{ name: "Downloads", type: "folder", icon: "folder", meta: "12 items • Modified yesterday" },
|
||||
{ name: "Pictures", type: "folder", icon: "folder", meta: "28 items • Modified 2 days ago" },
|
||||
{ name: "report.pdf", type: "file", icon: "insert_drive_file", meta: "PDF • 2.4 MB • Today" },
|
||||
{ name: "photo_001.jpg", type: "image", icon: "image", meta: "JPEG • 3.8 MB • Today" },
|
||||
{ name: "notes.txt", type: "document", icon: "description", meta: "Text • 1.2 KB • Yesterday" },
|
||||
{ name: "Music", type: "folder", icon: "folder", meta: "45 items • Modified last week" }
|
||||
];
|
||||
|
||||
this.initialize();
|
||||
this.render();
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.closeBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
this.os.closeAllPopups();
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
this.listElement.innerHTML = '';
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
this.fileData.forEach(file => {
|
||||
const fileEl = document.createElement('div');
|
||||
fileEl.className = 'file-item';
|
||||
fileEl.innerHTML = `
|
||||
<div class="file-icon file-${file.type}">
|
||||
<span class="material-icons">${file.icon}</span>
|
||||
</div>
|
||||
<div class="file-info">
|
||||
<span class="file-name">${file.name}</span>
|
||||
<span class="file-meta">${file.meta}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
fileEl.addEventListener('click', (e) => {
|
||||
this.listElement.querySelectorAll('.file-item').forEach(el => el.classList.remove('selected'));
|
||||
fileEl.classList.add('selected');
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
fragment.appendChild(fileEl);
|
||||
});
|
||||
|
||||
this.listElement.appendChild(fragment);
|
||||
}
|
||||
|
||||
open() {
|
||||
this.element.classList.add('active');
|
||||
this.os.components.taskbar.updateActiveButton('fileExplorer');
|
||||
}
|
||||
|
||||
close() {
|
||||
this.element.classList.remove('active');
|
||||
this.listElement.querySelectorAll('.file-item').forEach(el => el.classList.remove('selected'));
|
||||
this.os.components.taskbar.updateActiveButton(null);
|
||||
}
|
||||
}
|
||||
77
shelled/shelled-os-ui/src/js/components/Settings.js
Normal file
77
shelled/shelled-os-ui/src/js/components/Settings.js
Normal file
@ -0,0 +1,77 @@
|
||||
export default class Settings {
|
||||
constructor(os) {
|
||||
this.os = os;
|
||||
this.element = document.getElementById('settings');
|
||||
this.listElement = document.getElementById('settings-list');
|
||||
this.closeBtn = this.element.querySelector('.close-btn');
|
||||
|
||||
this.settingsData = [
|
||||
{ label: "Dark Mode", icon: "dark_mode", active: false },
|
||||
{ label: "Notifications", icon: "notifications", active: true },
|
||||
{ label: "Sound Effects", icon: "volume_up", active: true },
|
||||
{ label: "Bluetooth", icon: "bluetooth", active: false },
|
||||
{ label: "Location Services", icon: "location_on", active: false }
|
||||
];
|
||||
|
||||
this.initialize();
|
||||
this.render();
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.closeBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
this.os.closeAllPopups();
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
this.listElement.innerHTML = '';
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
this.settingsData.forEach((setting, index) => {
|
||||
const settingEl = document.createElement('div');
|
||||
settingEl.className = 'setting-item';
|
||||
|
||||
const activeClass = setting.active ? 'active' : '';
|
||||
|
||||
settingEl.innerHTML = `
|
||||
<div class="setting-info">
|
||||
<span class="material-icons setting-icon">${setting.icon}</span>
|
||||
<span class="setting-label">${setting.label}</span>
|
||||
</div>
|
||||
<div class="toggle-switch ${activeClass}" data-index="${index}">
|
||||
<div class="toggle-thumb"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const toggleSwitch = settingEl.querySelector('.toggle-switch');
|
||||
toggleSwitch.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
// Toggle active state locally
|
||||
this.settingsData[index].active = !this.settingsData[index].active;
|
||||
toggleSwitch.classList.toggle('active');
|
||||
});
|
||||
|
||||
// Make whole row clickable
|
||||
settingEl.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
// Find and click the toggle switch inside
|
||||
toggleSwitch.click();
|
||||
});
|
||||
|
||||
fragment.appendChild(settingEl);
|
||||
});
|
||||
|
||||
this.listElement.appendChild(fragment);
|
||||
}
|
||||
|
||||
open() {
|
||||
this.element.classList.add('active');
|
||||
this.os.components.taskbar.updateActiveButton('settings');
|
||||
}
|
||||
|
||||
close() {
|
||||
this.element.classList.remove('active');
|
||||
this.os.components.taskbar.updateActiveButton(null);
|
||||
}
|
||||
}
|
||||
92
shelled/shelled-os-ui/src/js/components/StartMenu.js
Normal file
92
shelled/shelled-os-ui/src/js/components/StartMenu.js
Normal file
@ -0,0 +1,92 @@
|
||||
export default class StartMenu {
|
||||
constructor(os) {
|
||||
this.os = os;
|
||||
this.element = document.getElementById('start-menu');
|
||||
this.gridElement = document.getElementById('app-grid');
|
||||
this.searchInput = document.getElementById('app-search');
|
||||
|
||||
this.appData = [
|
||||
{ name: "Angry Birds", icon: "mood_bad", color: "red", desc: "Mobile Game", letter: "A" },
|
||||
{ name: "Adobe Photoshop", icon: "edit", color: "green", desc: "Image Editor", letter: "A" },
|
||||
{ name: "Brave Browser", icon: "language", color: "blue", desc: "Web Browser", letter: "B" },
|
||||
{ name: "Books", icon: "book", color: "purple", desc: "Reading App", letter: "B" },
|
||||
{ name: "Chrome", icon: "code", color: "orange", desc: "Web Browser", letter: "C" },
|
||||
{ name: "Discord", icon: "chat", color: "blue", desc: "Communication", letter: "D" },
|
||||
{ name: "GarageBand", icon: "music_note", color: "yellow", desc: "macOS Music App", letter: "G" },
|
||||
{ name: "Movies & TV", icon: "movie", color: "purple", desc: "Media Player", letter: "M" },
|
||||
{ name: "Music", icon: "music_note", color: "green", desc: "Audio Player", letter: "M" },
|
||||
{ name: "Word", icon: "description", color: "default", desc: "Document Editor", letter: "W" },
|
||||
{ name: "YouTube", icon: "theaters", color: "orange", desc: "Video Platform", letter: "Y" }
|
||||
];
|
||||
|
||||
this.initialize();
|
||||
this.render(this.appData);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.searchInput.addEventListener('input', (e) => {
|
||||
const query = e.target.value.toLowerCase();
|
||||
const filteredApps = this.appData.filter(app =>
|
||||
app.name.toLowerCase().includes(query)
|
||||
);
|
||||
this.render(filteredApps, query !== '');
|
||||
});
|
||||
}
|
||||
|
||||
render(apps, isSearching = false) {
|
||||
this.gridElement.innerHTML = '';
|
||||
|
||||
if (apps.length === 0) {
|
||||
this.gridElement.innerHTML = '<div style="grid-column: 1/-1; text-align:center; padding: 20px; opacity:0.6;">No apps found</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
let currentLetter = '';
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
apps.forEach(app => {
|
||||
if (!isSearching && app.letter !== currentLetter) {
|
||||
currentLetter = app.letter;
|
||||
const divider = document.createElement('div');
|
||||
divider.className = 'letter-divider';
|
||||
divider.textContent = currentLetter;
|
||||
fragment.appendChild(divider);
|
||||
}
|
||||
|
||||
const appEl = document.createElement('div');
|
||||
appEl.className = 'app-item';
|
||||
appEl.innerHTML = `
|
||||
<div class="app-icon color-${app.color}">
|
||||
<span class="material-icons">${app.icon}</span>
|
||||
</div>
|
||||
<div class="app-info">
|
||||
<span class="app-name">${app.name}</span>
|
||||
<span class="app-desc">${app.desc}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
appEl.addEventListener('click', (e) => {
|
||||
// Remove selected from all
|
||||
this.gridElement.querySelectorAll('.app-item').forEach(el => el.classList.remove('selected'));
|
||||
appEl.classList.add('selected');
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
fragment.appendChild(appEl);
|
||||
});
|
||||
|
||||
this.gridElement.appendChild(fragment);
|
||||
}
|
||||
|
||||
open() {
|
||||
this.element.classList.add('active');
|
||||
this.os.components.taskbar.updateActiveButton('startMenu');
|
||||
}
|
||||
|
||||
close() {
|
||||
this.element.classList.remove('active');
|
||||
this.searchInput.value = '';
|
||||
this.render(this.appData); // Reset search
|
||||
this.os.components.taskbar.updateActiveButton(null);
|
||||
}
|
||||
}
|
||||
73
shelled/shelled-os-ui/src/js/components/Taskbar.js
Normal file
73
shelled/shelled-os-ui/src/js/components/Taskbar.js
Normal file
@ -0,0 +1,73 @@
|
||||
export default class Taskbar {
|
||||
constructor(os) {
|
||||
this.os = os;
|
||||
this.buttons = {
|
||||
start: document.getElementById('btn-start'),
|
||||
explorer: document.getElementById('btn-explorer'),
|
||||
browser: document.getElementById('btn-browser'),
|
||||
settings: document.getElementById('btn-settings')
|
||||
};
|
||||
this.clockElement = document.getElementById('clock');
|
||||
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
initialize() {
|
||||
// Start Menu Button
|
||||
this.buttons.start.addEventListener('click', (e) => {
|
||||
this.os.togglePopup('startMenu');
|
||||
});
|
||||
|
||||
// File Explorer Button
|
||||
this.buttons.explorer.addEventListener('click', (e) => {
|
||||
this.os.togglePopup('fileExplorer');
|
||||
});
|
||||
|
||||
// Browser Button
|
||||
this.buttons.browser.addEventListener('click', (e) => {
|
||||
this.os.togglePopup('browser');
|
||||
});
|
||||
|
||||
// Settings Button
|
||||
this.buttons.settings.addEventListener('click', (e) => {
|
||||
this.os.togglePopup('settings');
|
||||
});
|
||||
|
||||
this.startClock();
|
||||
}
|
||||
|
||||
updateActiveButton(componentName) {
|
||||
// Remove active class from all
|
||||
Object.values(this.buttons).forEach(btn => btn.classList.remove('active'));
|
||||
|
||||
// Add to mapped
|
||||
const map = {
|
||||
'startMenu': this.buttons.start,
|
||||
'fileExplorer': this.buttons.explorer,
|
||||
'browser': this.buttons.browser,
|
||||
'settings': this.buttons.settings
|
||||
};
|
||||
|
||||
if (componentName && map[componentName]) {
|
||||
map[componentName].classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
startClock() {
|
||||
const updateClock = () => {
|
||||
const now = new Date();
|
||||
let hours = now.getHours();
|
||||
let minutes = now.getMinutes();
|
||||
const ampm = hours >= 12 ? 'PM' : 'AM';
|
||||
|
||||
hours = hours % 12;
|
||||
hours = hours ? hours : 12; // 0 should be 12
|
||||
minutes = minutes < 10 ? '0' + minutes : minutes;
|
||||
|
||||
this.clockElement.textContent = `${hours}:${minutes} ${ampm}`;
|
||||
};
|
||||
|
||||
updateClock();
|
||||
setInterval(updateClock, 1000);
|
||||
}
|
||||
}
|
||||
60
shelled/shelled-os-ui/src/js/main.js
Normal file
60
shelled/shelled-os-ui/src/js/main.js
Normal file
@ -0,0 +1,60 @@
|
||||
import Taskbar from './components/Taskbar.js';
|
||||
import StartMenu from './components/StartMenu.js';
|
||||
import FileExplorer from './components/FileExplorer.js';
|
||||
import Browser from './components/Browser.js';
|
||||
import Settings from './components/Settings.js';
|
||||
|
||||
class OSController {
|
||||
constructor() {
|
||||
this.components = {};
|
||||
this.activePopup = null;
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
initialize() {
|
||||
// Initialize all components
|
||||
this.components.startMenu = new StartMenu(this);
|
||||
this.components.fileExplorer = new FileExplorer(this);
|
||||
this.components.browser = new Browser(this);
|
||||
this.components.settings = new Settings(this);
|
||||
|
||||
// Taskbar comes last as it needs references to others
|
||||
this.components.taskbar = new Taskbar(this);
|
||||
|
||||
// Global click to close popups
|
||||
document.addEventListener('click', (e) => {
|
||||
// Check if click was inside a popup or on a taskbar button
|
||||
const isPopupClick = e.target.closest('.popup');
|
||||
const isTaskbarBtnClick = e.target.closest('.taskbar-btn');
|
||||
|
||||
if (!isPopupClick && !isTaskbarBtnClick && this.activePopup) {
|
||||
this.closeAllPopups();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
togglePopup(componentName) {
|
||||
const component = this.components[componentName];
|
||||
if (!component) return;
|
||||
|
||||
if (this.activePopup === component) {
|
||||
this.closeAllPopups();
|
||||
} else {
|
||||
this.closeAllPopups();
|
||||
component.open();
|
||||
this.activePopup = component;
|
||||
}
|
||||
}
|
||||
|
||||
closeAllPopups() {
|
||||
if (this.activePopup) {
|
||||
this.activePopup.close();
|
||||
this.activePopup = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize on load
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
window.os = new OSController();
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user