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