├── .gitignore
├── README.md
├── index.html
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── public
├── apps
│ ├── edge.svg
│ ├── file-explorer.png
│ ├── search.svg
│ └── windows-terminal.png
├── fonts
│ └── segoe-ui-variable
│ │ ├── Segoe-UI-Variable-Static-Display-Bold.ttf
│ │ ├── Segoe-UI-Variable-Static-Display-Light.ttf
│ │ ├── Segoe-UI-Variable-Static-Display-Semibold.ttf
│ │ ├── Segoe-UI-Variable-Static-Display-Semilight.ttf
│ │ ├── Segoe-UI-Variable-Static-Display.ttf
│ │ ├── Segoe-UI-Variable-Static-Small-Bold.ttf
│ │ ├── Segoe-UI-Variable-Static-Small-Light.ttf
│ │ ├── Segoe-UI-Variable-Static-Small-Semibold.ttf
│ │ ├── Segoe-UI-Variable-Static-Small-Semilight.ttf
│ │ ├── Segoe-UI-Variable-Static-Small.ttf
│ │ ├── Segoe-UI-Variable-Static-Text-Bold.ttf
│ │ ├── Segoe-UI-Variable-Static-Text-Light.ttf
│ │ ├── Segoe-UI-Variable-Static-Text-Semibold.ttf
│ │ ├── Segoe-UI-Variable-Static-Text-Semilight.ttf
│ │ ├── Segoe-UI-Variable-Static-Text.ttf
│ │ └── SegoeUI-VF.ttf
├── icons
│ └── pin.png
├── og
│ └── og.jpg
├── system-icons
│ ├── Audio file.png
│ ├── Audio.png
│ ├── Blank.png
│ ├── Briefcase.png
│ ├── Camera.png
│ ├── Computer.png
│ ├── Control Panel.png
│ ├── Defrag.png
│ ├── Desktop.png
│ ├── Device 2.png
│ ├── Device.png
│ ├── Drives
│ │ ├── 1030.png
│ │ ├── 1032.png
│ │ ├── 1033.png
│ │ ├── 1034.png
│ │ ├── 1035.png
│ │ ├── 1036.png
│ │ ├── 1041.png
│ │ ├── 135.png
│ │ ├── 136.png
│ │ ├── 139.png
│ │ ├── 140.png
│ │ ├── 141.png
│ │ ├── 142.png
│ │ ├── 172.png
│ │ ├── 173.png
│ │ ├── 180.png
│ │ ├── 28.png
│ │ ├── 29.png
│ │ ├── 30.png
│ │ ├── 31.png
│ │ ├── 33.png
│ │ ├── 34.png
│ │ ├── 37.png
│ │ ├── 38.png
│ │ ├── 39.png
│ │ ├── 40.png
│ │ ├── 41.png
│ │ ├── 42.png
│ │ ├── 43.png
│ │ ├── 44.png
│ │ ├── 56.png
│ │ ├── 58.png
│ │ ├── 60.png
│ │ ├── 61.png
│ │ ├── 62.png
│ │ ├── 63.png
│ │ ├── 64.png
│ │ ├── 65.png
│ │ ├── 75.png
│ │ ├── 91.png
│ │ ├── 92.png
│ │ ├── 96.png
│ │ ├── Hardrive Windows.png
│ │ └── Hardrive.png
│ ├── Emblems
│ │ ├── 1010.png
│ │ ├── 1027.png
│ │ ├── 104.png
│ │ ├── 1042.png
│ │ ├── 105.png
│ │ ├── 106.png
│ │ ├── 107.png
│ │ ├── 1400.png
│ │ ├── 1401.png
│ │ ├── 1402.png
│ │ ├── 1403.png
│ │ ├── 1404.png
│ │ ├── 1405.png
│ │ ├── 157.png
│ │ ├── 163.png
│ │ ├── 164.png
│ │ ├── 169.png
│ │ ├── 170.png
│ │ └── 176.png
│ ├── Folder 3D.png
│ ├── Folder Blue.png
│ ├── Folder Contacts.png
│ ├── Folder Desktop.png
│ ├── Folder Documents.png
│ ├── Folder Downloads.png
│ ├── Folder Favourites.png
│ ├── Folder Fonts.png
│ ├── Folder Games.png
│ ├── Folder Green.png
│ ├── Folder Grey.png
│ ├── Folder Links 2.png
│ ├── Folder Links.png
│ ├── Folder Live - Back.png
│ ├── Folder Live - Front.png
│ ├── Folder Music.png
│ ├── Folder OneDrive.png
│ ├── Folder Open.png
│ ├── Folder Pictures.png
│ ├── Folder Search.png
│ ├── Folder Searches.png
│ ├── Folder User 2.png
│ ├── Folder User.png
│ ├── Folder Videos.png
│ ├── Folder check.png
│ ├── Folder.png
│ ├── Hardware.png
│ ├── Help 2.png
│ ├── Help.png
│ ├── History.png
│ ├── HomeGroup.png
│ ├── Hotspot.png
│ ├── Info.png
│ ├── Key.png
│ ├── Keyboard.png
│ ├── Library Library.png
│ ├── Library Music.png
│ ├── Library TV.png
│ ├── Library Videos.png
│ ├── Library.png
│ ├── Libray Documents.png
│ ├── Libray Pictures.png
│ ├── Link.png
│ ├── Lock.png
│ ├── Media.png
│ ├── Misc
│ │ ├── 1021.png
│ │ ├── 103.png
│ │ ├── 116.png
│ │ ├── 120.png
│ │ ├── 124.png
│ │ ├── 125.png
│ │ ├── 130.png
│ │ ├── 14.png
│ │ ├── 148.png
│ │ ├── 149.png
│ │ ├── 15.png
│ │ ├── 24.png
│ │ ├── 73.png
│ │ └── 94.png
│ ├── Multimedia file.png
│ ├── Music file.png
│ ├── Network.png
│ ├── Notes.png
│ ├── One Drive.png
│ ├── Personalization.png
│ ├── Phone.png
│ ├── Photos file.png
│ ├── Photos.png
│ ├── Pictures file.png
│ ├── Printer 2.png
│ ├── Printer.png
│ ├── Printer
│ │ ├── 26.png
│ │ ├── 45.png
│ │ ├── 48.png
│ │ ├── 49.png
│ │ ├── 50.png
│ │ ├── 52.png
│ │ └── 53.png
│ ├── Programs.png
│ ├── Quick Access.png
│ ├── Recent.png
│ ├── Region.png
│ ├── Run 1.png
│ ├── Run.png
│ ├── SHIDI_SHIELD_INTERNAL.png
│ ├── Scanner.png
│ ├── Screensaver.png
│ ├── Search.png
│ ├── Security.png
│ ├── Settings 2.png
│ ├── Settings.png
│ ├── Shield.png
│ ├── Stop.png
│ ├── Tasks.png
│ ├── Trash Empty.png
│ ├── Trash Full.png
│ ├── User.png
│ ├── Users.png
│ ├── Video device.png
│ ├── Video file.png
│ ├── Videos file.png
│ ├── Warning.png
│ ├── Zip.png
│ ├── config.png
│ ├── dll.png
│ ├── exe.png
│ └── x.png
└── wallpaper
│ ├── .pnpm-debug.log
│ ├── opened.webp
│ ├── shiroko.webp
│ ├── win11.jpg
│ └── win11.webp
├── src
├── App.tsx
├── apps
│ ├── file-explorer
│ │ ├── components
│ │ │ ├── atoms
│ │ │ │ ├── accordion
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── types.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── navigation-button
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── types.ts
│ │ │ │ ├── sidebar-group
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── types.ts
│ │ │ │ ├── sidebar-item
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── types.ts
│ │ │ │ └── status
│ │ │ │ │ └── index.tsx
│ │ │ ├── index.ts
│ │ │ ├── molecules
│ │ │ │ ├── index.ts
│ │ │ │ ├── navigation-bar
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── sidebar
│ │ │ │ │ └── index.tsx
│ │ │ │ └── toolbar
│ │ │ │ │ └── index.tsx
│ │ │ └── organisms
│ │ │ │ ├── index.ts
│ │ │ │ └── this-pc
│ │ │ │ └── index.tsx
│ │ ├── file-explorer.module.sass
│ │ ├── index.tsx
│ │ └── layouts
│ │ │ └── tiles
│ │ │ ├── app.tsx
│ │ │ ├── index.tsx
│ │ │ └── tiles.module.sass
│ └── index.ts
├── components
│ ├── atoms
│ │ ├── button
│ │ │ ├── button.module.sass
│ │ │ ├── index.tsx
│ │ │ └── types.ts
│ │ ├── index.ts
│ │ ├── text-input
│ │ │ ├── index.tsx
│ │ │ ├── text-input.module.sass
│ │ │ └── types.ts
│ │ └── tooltip
│ │ │ ├── index.tsx
│ │ │ ├── tooltip.module.sass
│ │ │ └── types.ts
│ └── index.ts
├── data
│ ├── apps.ts
│ └── default.tsx
├── layouts
│ ├── contextable
│ │ ├── index.tsx
│ │ └── types.ts
│ ├── index.ts
│ └── window
│ │ ├── components
│ │ └── resizeHandler.tsx
│ │ ├── hooks
│ │ ├── index.ts
│ │ ├── useManager.ts
│ │ └── useWindow.ts
│ │ ├── index.tsx
│ │ ├── types.ts
│ │ └── window.module.sass
├── main.tsx
├── modules
│ ├── context-menu
│ │ ├── components
│ │ │ ├── context-balloon
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ ├── context
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ └── index.ts
│ │ ├── context.module.sass
│ │ ├── index.tsx
│ │ └── services
│ │ │ └── index.ts
│ ├── sidebar
│ │ ├── components
│ │ │ ├── index.ts
│ │ │ └── organisms
│ │ │ │ ├── calendar
│ │ │ │ └── index.tsx
│ │ │ │ └── index.ts
│ │ ├── index.tsx
│ │ ├── sidebar.module.sass
│ │ └── stores
│ │ │ └── index.ts
│ ├── start
│ │ ├── components
│ │ │ ├── atoms
│ │ │ │ ├── card
│ │ │ │ │ ├── card.module.sass
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── types.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── li
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── types.ts
│ │ │ │ ├── overlay
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── pin
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── types.ts
│ │ │ │ ├── recommended
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── types.ts
│ │ │ │ ├── resetter
│ │ │ │ │ └── index.tsx
│ │ │ │ └── selection
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ ├── selection.module.sass
│ │ │ │ │ └── types.ts
│ │ │ ├── index.ts
│ │ │ ├── molecules
│ │ │ │ ├── all-apps
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── keyboard-listener
│ │ │ │ │ └── index.ts
│ │ │ │ ├── quick-view
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── recommended
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── search-bar
│ │ │ │ │ └── index.tsx
│ │ │ │ └── search-options
│ │ │ │ │ └── index.tsx
│ │ │ └── organisms
│ │ │ │ ├── index.ts
│ │ │ │ ├── overview
│ │ │ │ └── index.tsx
│ │ │ │ └── search
│ │ │ │ └── index.tsx
│ │ ├── index.tsx
│ │ ├── layouts
│ │ │ ├── animated-page
│ │ │ │ ├── animate-page.module.sass
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ ├── index.ts
│ │ │ └── start
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ ├── stores
│ │ │ ├── index.ts
│ │ │ ├── overview.ts
│ │ │ └── search.ts
│ │ └── styles.module.sass
│ └── taskbar
│ │ ├── components
│ │ ├── atoms
│ │ │ ├── index.ts
│ │ │ ├── taskbar-app
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ ├── taskbar-item
│ │ │ │ ├── index.tsx
│ │ │ │ └── types.ts
│ │ │ └── windows-button
│ │ │ │ └── index.tsx
│ │ ├── index.ts
│ │ └── molecules
│ │ │ ├── datetime
│ │ │ └── index.tsx
│ │ │ ├── hidden
│ │ │ └── index.tsx
│ │ │ ├── index.tsx
│ │ │ ├── language
│ │ │ └── index.tsx
│ │ │ └── network
│ │ │ └── index.tsx
│ │ ├── index.tsx
│ │ └── styles.module.sass
├── services
│ └── ease.ts
├── stores
│ ├── apps
│ │ └── index.tsx
│ ├── context-menu
│ │ └── index.ts
│ ├── index.ts
│ └── start
│ │ └── index.ts
├── styles
│ ├── app.sass
│ ├── fonts.sass
│ ├── index.sass
│ └── tailwind.css
├── types.ts
└── vite-env.d.ts
├── tailwind.config.js
├── tsconfig.json
└── vite.config.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sun Valley
2 | UI port of Windows 11 to the web
3 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Sun Valley
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
66 |
67 |
68 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sun-valley",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "tsc && vite build",
7 | "serve": "serve -p 3000 dist",
8 | "start": "serve -p 3000 dist"
9 | },
10 | "dependencies": {
11 | "@tailwindcss/aspect-ratio": "^0.3.0",
12 | "dayjs": "^1.10.7",
13 | "framer-motion": "^4.1.17",
14 | "jotai": "^1.4.0",
15 | "nanoid": "^3.1.30",
16 | "preact": "^10.5.15",
17 | "react": "^17.0.0",
18 | "react-dom": "^17.0.0",
19 | "react-feather": "^2.0.9",
20 | "tailwindcss": "^2.2.17"
21 | },
22 | "devDependencies": {
23 | "@prefresh/vite": "^2.2.3",
24 | "@types/node": "^16.11.0",
25 | "@types/react": "^17.0.0",
26 | "@types/react-dom": "^17.0.0",
27 | "@vitejs/plugin-react": "^1.0.0",
28 | "autoprefixer": "^10.3.7",
29 | "postcss": "^8.3.9",
30 | "sass": "^1.43.2",
31 | "serve": "^12.0.1",
32 | "typescript": "^4.3.2",
33 | "vite": "^2.6.4"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {},
4 | tailwindcss: {}
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/public/apps/edge.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/apps/file-explorer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/apps/file-explorer.png
--------------------------------------------------------------------------------
/public/apps/search.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/apps/windows-terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/apps/windows-terminal.png
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display-Bold.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display-Light.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display-Semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display-Semibold.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display-Semilight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display-Semilight.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small-Bold.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small-Light.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small-Semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small-Semibold.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small-Semilight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small-Semilight.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text-Bold.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text-Light.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text-Semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text-Semibold.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text-Semilight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text-Semilight.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text.ttf
--------------------------------------------------------------------------------
/public/fonts/segoe-ui-variable/SegoeUI-VF.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/fonts/segoe-ui-variable/SegoeUI-VF.ttf
--------------------------------------------------------------------------------
/public/icons/pin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/icons/pin.png
--------------------------------------------------------------------------------
/public/og/og.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/og/og.jpg
--------------------------------------------------------------------------------
/public/system-icons/Audio file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Audio file.png
--------------------------------------------------------------------------------
/public/system-icons/Audio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Audio.png
--------------------------------------------------------------------------------
/public/system-icons/Blank.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Blank.png
--------------------------------------------------------------------------------
/public/system-icons/Briefcase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Briefcase.png
--------------------------------------------------------------------------------
/public/system-icons/Camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Camera.png
--------------------------------------------------------------------------------
/public/system-icons/Computer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Computer.png
--------------------------------------------------------------------------------
/public/system-icons/Control Panel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Control Panel.png
--------------------------------------------------------------------------------
/public/system-icons/Defrag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Defrag.png
--------------------------------------------------------------------------------
/public/system-icons/Desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Desktop.png
--------------------------------------------------------------------------------
/public/system-icons/Device 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Device 2.png
--------------------------------------------------------------------------------
/public/system-icons/Device.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Device.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/1030.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/1030.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/1032.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/1032.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/1033.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/1033.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/1034.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/1034.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/1035.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/1035.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/1036.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/1036.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/1041.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/1041.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/135.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/135.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/136.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/136.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/139.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/139.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/140.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/140.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/141.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/141.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/142.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/142.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/172.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/172.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/173.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/173.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/180.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/28.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/28.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/29.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/30.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/30.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/31.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/31.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/33.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/33.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/34.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/34.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/37.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/37.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/38.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/39.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/39.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/40.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/41.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/41.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/42.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/42.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/43.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/43.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/44.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/44.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/56.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/56.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/58.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/60.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/61.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/61.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/62.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/62.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/63.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/63.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/64.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/65.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/65.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/75.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/75.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/91.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/91.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/92.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/92.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/96.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/Hardrive Windows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/Hardrive Windows.png
--------------------------------------------------------------------------------
/public/system-icons/Drives/Hardrive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Drives/Hardrive.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/1010.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/1010.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/1027.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/1027.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/104.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/104.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/1042.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/1042.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/105.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/105.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/106.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/106.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/107.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/107.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/1400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/1400.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/1401.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/1401.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/1402.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/1402.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/1403.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/1403.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/1404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/1404.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/1405.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/1405.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/157.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/157.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/163.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/163.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/164.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/164.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/169.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/169.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/170.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/170.png
--------------------------------------------------------------------------------
/public/system-icons/Emblems/176.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Emblems/176.png
--------------------------------------------------------------------------------
/public/system-icons/Folder 3D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder 3D.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Blue.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Contacts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Contacts.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Desktop.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Documents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Documents.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Downloads.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Downloads.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Favourites.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Favourites.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Fonts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Fonts.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Games.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Games.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Green.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Grey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Grey.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Links 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Links 2.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Links.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Links.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Live - Back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Live - Back.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Live - Front.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Live - Front.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Music.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Music.png
--------------------------------------------------------------------------------
/public/system-icons/Folder OneDrive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder OneDrive.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Open.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Pictures.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Pictures.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Search.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Searches.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Searches.png
--------------------------------------------------------------------------------
/public/system-icons/Folder User 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder User 2.png
--------------------------------------------------------------------------------
/public/system-icons/Folder User.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder User.png
--------------------------------------------------------------------------------
/public/system-icons/Folder Videos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder Videos.png
--------------------------------------------------------------------------------
/public/system-icons/Folder check.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder check.png
--------------------------------------------------------------------------------
/public/system-icons/Folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Folder.png
--------------------------------------------------------------------------------
/public/system-icons/Hardware.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Hardware.png
--------------------------------------------------------------------------------
/public/system-icons/Help 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Help 2.png
--------------------------------------------------------------------------------
/public/system-icons/Help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Help.png
--------------------------------------------------------------------------------
/public/system-icons/History.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/History.png
--------------------------------------------------------------------------------
/public/system-icons/HomeGroup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/HomeGroup.png
--------------------------------------------------------------------------------
/public/system-icons/Hotspot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Hotspot.png
--------------------------------------------------------------------------------
/public/system-icons/Info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Info.png
--------------------------------------------------------------------------------
/public/system-icons/Key.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Key.png
--------------------------------------------------------------------------------
/public/system-icons/Keyboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Keyboard.png
--------------------------------------------------------------------------------
/public/system-icons/Library Library.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Library Library.png
--------------------------------------------------------------------------------
/public/system-icons/Library Music.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Library Music.png
--------------------------------------------------------------------------------
/public/system-icons/Library TV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Library TV.png
--------------------------------------------------------------------------------
/public/system-icons/Library Videos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Library Videos.png
--------------------------------------------------------------------------------
/public/system-icons/Library.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Library.png
--------------------------------------------------------------------------------
/public/system-icons/Libray Documents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Libray Documents.png
--------------------------------------------------------------------------------
/public/system-icons/Libray Pictures.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Libray Pictures.png
--------------------------------------------------------------------------------
/public/system-icons/Link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Link.png
--------------------------------------------------------------------------------
/public/system-icons/Lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Lock.png
--------------------------------------------------------------------------------
/public/system-icons/Media.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Media.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/1021.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/1021.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/103.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/103.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/116.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/116.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/120.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/124.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/124.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/125.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/130.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/130.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/14.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/148.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/148.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/149.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/149.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/15.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/24.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/73.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/73.png
--------------------------------------------------------------------------------
/public/system-icons/Misc/94.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Misc/94.png
--------------------------------------------------------------------------------
/public/system-icons/Multimedia file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Multimedia file.png
--------------------------------------------------------------------------------
/public/system-icons/Music file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Music file.png
--------------------------------------------------------------------------------
/public/system-icons/Network.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Network.png
--------------------------------------------------------------------------------
/public/system-icons/Notes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Notes.png
--------------------------------------------------------------------------------
/public/system-icons/One Drive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/One Drive.png
--------------------------------------------------------------------------------
/public/system-icons/Personalization.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Personalization.png
--------------------------------------------------------------------------------
/public/system-icons/Phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Phone.png
--------------------------------------------------------------------------------
/public/system-icons/Photos file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Photos file.png
--------------------------------------------------------------------------------
/public/system-icons/Photos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Photos.png
--------------------------------------------------------------------------------
/public/system-icons/Pictures file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Pictures file.png
--------------------------------------------------------------------------------
/public/system-icons/Printer 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Printer 2.png
--------------------------------------------------------------------------------
/public/system-icons/Printer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Printer.png
--------------------------------------------------------------------------------
/public/system-icons/Printer/26.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Printer/26.png
--------------------------------------------------------------------------------
/public/system-icons/Printer/45.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Printer/45.png
--------------------------------------------------------------------------------
/public/system-icons/Printer/48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Printer/48.png
--------------------------------------------------------------------------------
/public/system-icons/Printer/49.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Printer/49.png
--------------------------------------------------------------------------------
/public/system-icons/Printer/50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Printer/50.png
--------------------------------------------------------------------------------
/public/system-icons/Printer/52.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Printer/52.png
--------------------------------------------------------------------------------
/public/system-icons/Printer/53.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Printer/53.png
--------------------------------------------------------------------------------
/public/system-icons/Programs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Programs.png
--------------------------------------------------------------------------------
/public/system-icons/Quick Access.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Quick Access.png
--------------------------------------------------------------------------------
/public/system-icons/Recent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Recent.png
--------------------------------------------------------------------------------
/public/system-icons/Region.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Region.png
--------------------------------------------------------------------------------
/public/system-icons/Run 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Run 1.png
--------------------------------------------------------------------------------
/public/system-icons/Run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Run.png
--------------------------------------------------------------------------------
/public/system-icons/SHIDI_SHIELD_INTERNAL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/SHIDI_SHIELD_INTERNAL.png
--------------------------------------------------------------------------------
/public/system-icons/Scanner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Scanner.png
--------------------------------------------------------------------------------
/public/system-icons/Screensaver.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Screensaver.png
--------------------------------------------------------------------------------
/public/system-icons/Search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Search.png
--------------------------------------------------------------------------------
/public/system-icons/Security.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Security.png
--------------------------------------------------------------------------------
/public/system-icons/Settings 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Settings 2.png
--------------------------------------------------------------------------------
/public/system-icons/Settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Settings.png
--------------------------------------------------------------------------------
/public/system-icons/Shield.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Shield.png
--------------------------------------------------------------------------------
/public/system-icons/Stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Stop.png
--------------------------------------------------------------------------------
/public/system-icons/Tasks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Tasks.png
--------------------------------------------------------------------------------
/public/system-icons/Trash Empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Trash Empty.png
--------------------------------------------------------------------------------
/public/system-icons/Trash Full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Trash Full.png
--------------------------------------------------------------------------------
/public/system-icons/User.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/User.png
--------------------------------------------------------------------------------
/public/system-icons/Users.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Users.png
--------------------------------------------------------------------------------
/public/system-icons/Video device.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Video device.png
--------------------------------------------------------------------------------
/public/system-icons/Video file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Video file.png
--------------------------------------------------------------------------------
/public/system-icons/Videos file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Videos file.png
--------------------------------------------------------------------------------
/public/system-icons/Warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Warning.png
--------------------------------------------------------------------------------
/public/system-icons/Zip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/Zip.png
--------------------------------------------------------------------------------
/public/system-icons/config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/config.png
--------------------------------------------------------------------------------
/public/system-icons/dll.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/dll.png
--------------------------------------------------------------------------------
/public/system-icons/exe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/exe.png
--------------------------------------------------------------------------------
/public/system-icons/x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/system-icons/x.png
--------------------------------------------------------------------------------
/public/wallpaper/opened.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/wallpaper/opened.webp
--------------------------------------------------------------------------------
/public/wallpaper/shiroko.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/wallpaper/shiroko.webp
--------------------------------------------------------------------------------
/public/wallpaper/win11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/wallpaper/win11.jpg
--------------------------------------------------------------------------------
/public/wallpaper/win11.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SaltyAom/sun-valley/348a51cc99b96a0cd680734a155a9fcf722ddfac/public/wallpaper/win11.webp
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useApps } from '@stores/apps'
2 |
3 | import { AnimatePresence } from 'framer-motion'
4 |
5 | import { Contextable, Window } from '@layouts'
6 |
7 | import Taskbar from './modules/taskbar'
8 | import Start from './modules/start'
9 | import ContextMenu from './modules/context-menu'
10 | import Sidebar from './modules/sidebar'
11 |
12 | import { desktopContextMenu } from '@data/default'
13 |
14 | const App = () => {
15 | const [apps] = useApps()
16 |
17 | return (
18 |
19 |
28 |
29 |
30 | {apps.map((app) => (
31 |
32 | ))}
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | )
43 | }
44 |
45 | export default App
46 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/atoms/accordion/index.tsx:
--------------------------------------------------------------------------------
1 | import { useReducer } from 'react'
2 | import { AnimatePresence, motion } from 'framer-motion'
3 |
4 | import { ChevronRight } from 'react-feather'
5 |
6 | import style from '../../../file-explorer.module.sass'
7 |
8 | import type { AccordionComponent } from './types'
9 |
10 | const Accordion: AccordionComponent = ({ title, children }) => {
11 | const [isOpen, toggle] = useReducer((v) => !v, true)
12 |
13 | return (
14 | <>
15 |
20 |
21 | {isOpen && (
22 |
23 | {children}
24 |
25 | )}
26 |
27 | >
28 | )
29 | }
30 |
31 | export default Accordion
32 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/atoms/accordion/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent } from "react"
2 |
3 | export interface AccordionProps {
4 | title: string
5 | }
6 |
7 | export type AccordionComponent = FunctionComponent
8 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/atoms/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Accordion } from './accordion'
2 | export { default as NavigationButton } from './navigation-button'
3 | export { default as SidebarGroup } from './sidebar-group'
4 | export { default as SidebarItem } from './sidebar-item'
5 | export { default as Status } from './status'
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/atoms/navigation-button/index.tsx:
--------------------------------------------------------------------------------
1 | import type { NavigationButtonComponent } from "./types"
2 |
3 | const NavigationButton: NavigationButtonComponent = ({ icon: Icon }) => {
4 | return (
5 |
8 | )
9 | }
10 |
11 | export default NavigationButton
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/atoms/navigation-button/types.ts:
--------------------------------------------------------------------------------
1 | import type { DetailsHTMLAttributes, FunctionComponent } from "react"
2 |
3 | import type { Icon } from "react-feather"
4 |
5 | export interface NavigationButtonProps extends DetailsHTMLAttributes {
6 | icon: Icon
7 | }
8 |
9 | export type NavigationButtonComponent = FunctionComponent
10 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/atoms/sidebar-group/index.tsx:
--------------------------------------------------------------------------------
1 | import { useReducer } from 'react'
2 |
3 | import { SidebarItem } from '..'
4 |
5 | import type { SideBarGroupComponent } from './types'
6 |
7 | const SidebarGroup: SideBarGroupComponent = ({ title, items, icon }) => {
8 | const [isOpen, toggle] = useReducer((v) => !v, true)
9 |
10 | return (
11 |
12 |
19 | {isOpen && }
20 |
21 | )
22 | }
23 |
24 | export default SidebarGroup
25 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/atoms/sidebar-group/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent, ReactNode } from "react"
2 |
3 | export interface SidebarGroupProps {
4 | title: string
5 | icon?: string
6 | items: ReactNode
7 | }
8 |
9 | export type SideBarGroupComponent = FunctionComponent
10 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/atoms/sidebar-item/index.tsx:
--------------------------------------------------------------------------------
1 | import { ChevronRight, MapPin } from 'react-feather'
2 |
3 | import style from '../../../file-explorer.module.sass'
4 |
5 | import type { SideBarItemComponent } from './types'
6 |
7 | const SidebarItem: SideBarItemComponent = ({
8 | title,
9 | chevron = false,
10 | chevronDown = false,
11 | pinned = false,
12 | icon,
13 | className = '',
14 | ...props
15 | }) => {
16 | return (
17 |
38 | )
39 | }
40 |
41 | export default SidebarItem
42 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/atoms/sidebar-item/types.ts:
--------------------------------------------------------------------------------
1 | import type { DetailsHTMLAttributes, FunctionComponent } from "react"
2 |
3 | export interface SidebarItemProps extends DetailsHTMLAttributes {
4 | title?: string
5 | icon?: string
6 | chevron?: boolean
7 | chevronDown?: boolean
8 | pinned?: boolean
9 | }
10 |
11 | export type SideBarItemComponent = FunctionComponent
12 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/atoms/status/index.tsx:
--------------------------------------------------------------------------------
1 | import { AlignJustify, Square } from 'react-feather'
2 |
3 | const Status = () => {
4 | return (
5 |
21 | )
22 | }
23 |
24 | export default Status
25 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/index.ts:
--------------------------------------------------------------------------------
1 | export {
2 | Accordion,
3 | NavigationButton,
4 | SidebarGroup,
5 | SidebarItem,
6 | Status
7 | } from './atoms'
8 | export { NavigationBar, Sidebar, Toolbar } from './molecules'
9 | export { ThisPC } from './organisms'
10 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/molecules/index.ts:
--------------------------------------------------------------------------------
1 | export { default as NavigationBar } from './navigation-bar'
2 | export { default as Sidebar } from './sidebar'
3 | export { default as Toolbar } from './toolbar'
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/molecules/navigation-bar/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | ArrowLeft,
3 | ArrowRight,
4 | ChevronDown,
5 | ArrowUp,
6 | Search
7 | } from 'react-feather'
8 |
9 | import { NavigationButton } from '../..'
10 |
11 | const NavigationBar = () => {
12 | return (
13 |
39 | )
40 | }
41 |
42 | export default NavigationBar
43 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/molecules/sidebar/index.tsx:
--------------------------------------------------------------------------------
1 | import { SidebarGroup, SidebarItem } from '../..'
2 |
3 | const Sidebar = () => {
4 | return (
5 |
40 | )
41 | }
42 |
43 | export default Sidebar
44 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/molecules/toolbar/index.tsx:
--------------------------------------------------------------------------------
1 | import { ChevronDown, MoreHorizontal, Square } from 'react-feather'
2 |
3 | import style from '../../../file-explorer.module.sass'
4 |
5 | const Toolbar = () => {
6 | return (
7 |
48 | )
49 | }
50 |
51 | export default Toolbar
52 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/organisms/index.ts:
--------------------------------------------------------------------------------
1 | export { default as ThisPC } from './this-pc'
--------------------------------------------------------------------------------
/src/apps/file-explorer/components/organisms/this-pc/index.tsx:
--------------------------------------------------------------------------------
1 | import TileLayout from '@apps/file-explorer/layouts/tiles'
2 |
3 | import { Accordion } from '@apps/file-explorer/components'
4 |
5 | const ThisPC = () => {
6 | return (
7 | <>
8 |
9 |
37 |
38 |
39 |
51 |
52 | >
53 | )
54 | }
55 |
56 | export default ThisPC
57 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/file-explorer.module.sass:
--------------------------------------------------------------------------------
1 | .action
2 | @apply text-gray-400 text-sm hover:bg-opacity-5 hover:bg-gray-600 focus:bg-opacity-5 focus:bg-gray-600 transition-colors p-2 rounded
3 |
4 | .divider
5 | @apply inline-block w-[1px] h-full bg-gray-300
6 |
7 | .sidebar-icon
8 | stroke-width: 1.5
9 | width: 18px
10 | height: 18px
11 | transform: scale(.9)
12 |
13 | .pin
14 | transform: scaleX(-1)
15 | opacity: .35
16 |
17 | .chevron-down
18 | transform: rotate(90deg)
--------------------------------------------------------------------------------
/src/apps/file-explorer/index.tsx:
--------------------------------------------------------------------------------
1 | import { NavigationBar, Status, Toolbar, Sidebar, ThisPC } from './components'
2 |
3 | const FileExplorer = () => {
4 | return (
5 |
6 |
7 |
8 |
14 |
15 |
16 | )
17 | }
18 |
19 | export default FileExplorer
20 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/layouts/tiles/app.tsx:
--------------------------------------------------------------------------------
1 | import type { AppLauncherComponent } from 'types'
2 |
3 | const TileApp: AppLauncherComponent = ({ app: { name, icon } }) => {
4 | return (
5 |
11 | )
12 | }
13 |
14 | export default TileApp
15 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/layouts/tiles/index.tsx:
--------------------------------------------------------------------------------
1 | import TileApp from './app'
2 |
3 | import type { AppCollectionComponent } from 'types'
4 |
5 | import style from './tiles.module.sass'
6 |
7 | const TileLayout: AppCollectionComponent = ({ apps }) => {
8 | return (
9 |
10 | {apps.map((app) => (
11 |
12 | ))}
13 |
14 | )
15 | }
16 |
17 | export default TileLayout
18 |
--------------------------------------------------------------------------------
/src/apps/file-explorer/layouts/tiles/tiles.module.sass:
--------------------------------------------------------------------------------
1 | .tile
2 | @apply grid
3 | grid-template-columns: repeat(auto-fill,minmax(200px, 1fr))
--------------------------------------------------------------------------------
/src/apps/index.ts:
--------------------------------------------------------------------------------
1 | export { default as FileExplorer } from './file-explorer'
--------------------------------------------------------------------------------
/src/components/atoms/button/button.module.sass:
--------------------------------------------------------------------------------
1 | .button
2 | border-bottom-color: rgb(209, 213, 219) !important
3 |
--------------------------------------------------------------------------------
/src/components/atoms/button/index.tsx:
--------------------------------------------------------------------------------
1 | import type { ButtonComponent } from './types'
2 |
3 | import styles from './button.module.sass'
4 |
5 | const Button: ButtonComponent = ({ children, className, ...props }) => (
6 |
12 | )
13 |
14 | export default Button
15 |
--------------------------------------------------------------------------------
/src/components/atoms/button/types.ts:
--------------------------------------------------------------------------------
1 | import type { ButtonHTMLAttributes, FunctionComponent } from 'react'
2 |
3 | export interface ButtonProps extends ButtonHTMLAttributes {}
4 |
5 | export type ButtonComponent = FunctionComponent
6 |
--------------------------------------------------------------------------------
/src/components/atoms/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Button } from './button'
2 | export { default as TextInput } from './text-input'
3 | export { default as Tooltip } from './tooltip'
--------------------------------------------------------------------------------
/src/components/atoms/text-input/index.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react'
2 |
3 | import type { TextInputComponent } from './types'
4 |
5 | import styles from './text-input.module.sass'
6 |
7 | const TextInput: TextInputComponent = ({
8 | prefix = null,
9 | label,
10 | name = label,
11 | className = '',
12 | autoFocus = false,
13 | ...props
14 | }) => {
15 | const input = useRef(null)
16 |
17 | const focusInput = () => {
18 | input.current?.focus()
19 | }
20 |
21 | useEffect(() => {
22 | if (autoFocus) focusInput()
23 | }, [])
24 |
25 | return (
26 |
30 | {prefix}
31 |
39 |
40 | )
41 | }
42 |
43 | export default TextInput
44 |
--------------------------------------------------------------------------------
/src/components/atoms/text-input/text-input.module.sass:
--------------------------------------------------------------------------------
1 | .text-input
2 | @apply transition-colors transition-shadow transition-opacity
3 |
4 | &:hover
5 | @apply opacity-75
6 |
7 | &:focus-within
8 | @apply opacity-100
9 | box-shadow: 0 1px 0 0 rgba(0,123,255), 0 0 2px rgba(0,0,0,.075)
10 | border-bottom-color: rgba(0,123,255)
--------------------------------------------------------------------------------
/src/components/atoms/text-input/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent, HTMLProps } from 'react'
2 |
3 | export interface TextInputProps {
4 | name?: string
5 | label: string
6 | prefix?: JSX.Element
7 | className?: string
8 | autoFocus?: boolean
9 | }
10 |
11 | export type TextInputComponent = FunctionComponent>
12 |
--------------------------------------------------------------------------------
/src/components/atoms/tooltip/index.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useRef, useCallback } from 'react'
2 |
3 | import { motion, AnimatePresence } from 'framer-motion'
4 |
5 | import styles from './tooltip.module.sass'
6 |
7 | import type { TooltipComponent } from './types'
8 |
9 | const Tooltip: TooltipComponent = ({
10 | children,
11 | title,
12 | className = '',
13 | fluid = false,
14 | tooltipClassName = '',
15 | tooltipStyle = {}
16 | }) => {
17 | const [isPeeking, updatePeek] = useState(false)
18 | const [fadingOut, updateFadingOut] = useState(false)
19 | const timeout = useRef(null)
20 |
21 | const peek = useCallback(() => {
22 | if (timeout.current) clearTimeout(timeout.current)
23 |
24 | // @ts-ignore
25 | timeout.current = setTimeout(() => {
26 | updatePeek(true)
27 | requestAnimationFrame(() => {
28 | requestAnimationFrame(() => {
29 | updateFadingOut(true)
30 | })
31 | })
32 |
33 | if (timeout.current) clearTimeout(timeout.current)
34 | }, 800)
35 | }, [])
36 |
37 | const unpeek = useCallback(() => {
38 | updateFadingOut(false)
39 | requestAnimationFrame(() => {
40 | requestAnimationFrame(() => {
41 | updatePeek(false)
42 |
43 | if (timeout.current) clearTimeout(timeout.current)
44 | })
45 | })
46 | }, [])
47 |
48 | return (
49 |
50 |
51 | {isPeeking && (
52 |
66 | {title}
67 |
68 | )}
69 |
70 |
77 |
78 | )
79 | }
80 |
81 | export default Tooltip
82 |
--------------------------------------------------------------------------------
/src/components/atoms/tooltip/tooltip.module.sass:
--------------------------------------------------------------------------------
1 | .tooltip
2 | @apply absolute flex flex-col justify-end items-center bottom-14 opacity-0
3 | width: 192px
4 | pointer-events: none
5 | transform: translateX(-76px)
6 | transition: opacity 0.16s
7 |
8 | &.-fluid
9 | transform: translateX(-26px)
10 |
11 | &.-active
12 | @apply opacity-100
13 |
14 | & > .tip
15 | @apply text-xs px-3 py-2 rounded
--------------------------------------------------------------------------------
/src/components/atoms/tooltip/types.ts:
--------------------------------------------------------------------------------
1 | import type { CSSProperties, FunctionComponent } from "react"
2 |
3 | export interface TooltipProps {
4 | title: string
5 | className?: string
6 | tooltipClassName?: string
7 | tooltipStyle?: CSSProperties
8 | fluid?: boolean
9 | }
10 |
11 | export type TooltipComponent = FunctionComponent
12 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export { Button, TextInput, Tooltip } from './atoms'
2 |
--------------------------------------------------------------------------------
/src/data/apps.ts:
--------------------------------------------------------------------------------
1 | import { lazy } from 'react'
2 | import type { LazyExoticComponent } from "react"
3 |
4 | export interface App {
5 | name: string
6 | icon: string
7 | short?: string
8 | app?: LazyExoticComponent
9 | className?: string
10 | }
11 |
12 | export type Apps = App[]
13 |
14 | const apps = [
15 | {
16 | name: 'Microsoft Edge',
17 | icon: '/apps/edge.svg',
18 | short: 'Edge'
19 | },
20 | {
21 | name: 'File Explorer',
22 | icon: '/apps/file-explorer.png',
23 | short: 'Explorer',
24 | app: lazy(() => import("@apps/file-explorer")),
25 | className: 'bg-opacity-75'
26 | },
27 | {
28 | name: 'Window Terminal',
29 | icon: '/apps/windows-terminal.png',
30 | short: 'Terminal'
31 | }
32 | ]
33 |
34 | export const appsMap = apps
35 | .map((app) => ({
36 | [app.name]: app
37 | }))
38 | .reduce((acc, cur) => ({ ...acc, ...cur }))
39 |
40 | export default appsMap
41 |
--------------------------------------------------------------------------------
/src/data/default.tsx:
--------------------------------------------------------------------------------
1 | import { Context } from '@modules/context-menu'
2 |
3 | export const desktopContextMenu = [
4 | [
5 | ,
10 | ,
11 |
12 | ],
13 | [
14 | ,
15 |
16 | ],
17 | []
18 | ]}
19 | />,
20 | ,
25 | ,
26 | ,
27 |
28 | ]
29 | ]}
30 | />,
31 |
32 | ],
33 | [
34 | ,
39 | ,
40 |
44 | ]
45 | ]}
46 | />
47 | ],
48 | [, ],
49 | [],
50 | [
51 | {
55 | if (
56 | document.fullscreenElement ||
57 | // @ts-ignore
58 | document.webkitFullscreenElement
59 | )
60 | if (document.exitFullscreen) document.exitFullscreen()
61 | // @ts-ignore
62 | else if (document.webkitCancelFullScreen)
63 | // @ts-ignore
64 | document.webkitCancelFullScreen()
65 |
66 | const { body } = document
67 |
68 | if (body.requestFullscreen) body.requestFullscreen()
69 | // @ts-ignore
70 | else if (body.webkitRequestFullscreen)
71 | // @ts-ignore
72 | body.webkitRequestFullscreen()
73 | }}
74 | />
75 | ],
76 | [
77 | ]
93 | ]}
94 | />
95 | ]
96 | ]}
97 | />
98 | ]
99 | ]}
100 | />
101 | ]
102 | ]}
103 | />
104 | ]
105 | ]
106 |
--------------------------------------------------------------------------------
/src/layouts/contextable/index.tsx:
--------------------------------------------------------------------------------
1 | import { useRef } from 'react'
2 | import type { DOMAttributes } from 'react'
3 |
4 | import { useContextMenu } from '@stores/context-menu'
5 |
6 | import type { ContextableComponent } from './types'
7 |
8 | const Contextable: ContextableComponent = ({
9 | contexts,
10 | className = 'flex w-full h-full',
11 | children
12 | }) => {
13 | const deferTouch = useRef()
14 |
15 | const [, dispatchContextMenu] = useContextMenu()
16 |
17 | const showContextMenu = (pageX: number, pageY: number) => {
18 | dispatchContextMenu({
19 | type: 'clear'
20 | })
21 |
22 | dispatchContextMenu({
23 | type: 'append',
24 | position: {
25 | top: pageY + 1,
26 | left: pageX + 1
27 | },
28 | contexts
29 | })
30 | }
31 |
32 | const handleClick: DOMAttributes['onClick'] = (event) => {
33 | event.preventDefault()
34 |
35 | if(deferTouch.current) return
36 |
37 | const { pageX, pageY } = event
38 |
39 | showContextMenu(pageX, pageY)
40 | }
41 |
42 | const handleTouch: DOMAttributes['onTouchStart'] = (event) => {
43 | deferTouch.current = setTimeout(() => {
44 | const { touches } = event
45 | const { pageX, pageY } = touches[0]
46 |
47 | showContextMenu(pageX, pageY)
48 |
49 | requestAnimationFrame(() => {
50 | deferTouch.current = null
51 | })
52 | }, 625)
53 | }
54 |
55 | const handleUntouch = () => {
56 | if(deferTouch.current)
57 | clearTimeout(deferTouch.current)
58 |
59 | deferTouch.current = null
60 | }
61 |
62 | return (
63 |
69 | {children}
70 |
71 | )
72 | }
73 |
74 | export default Contextable
75 |
--------------------------------------------------------------------------------
/src/layouts/contextable/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent } from "react"
2 |
3 | export interface ContextableProps {
4 | contexts: JSX.Element[][]
5 | className?: string
6 | }
7 |
8 | export type ContextableComponent = FunctionComponent
9 |
--------------------------------------------------------------------------------
/src/layouts/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Contextable } from './contextable'
2 | export { default as Window } from './window'
--------------------------------------------------------------------------------
/src/layouts/window/components/resizeHandler.tsx:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent } from 'react'
2 |
3 | import { Resize } from '../hooks'
4 |
5 | import style from '../window.module.sass'
6 |
7 | const ResizeHandler: FunctionComponent<{
8 | startResize: (action: Resize) => () => void
9 | }> = ({ startResize }) => (
10 | <>
11 |
16 |
21 |
26 |
31 |
36 |
37 | {/* Top right corner to get around close button */}
38 |
43 |
48 |
49 |
54 |
59 | >
60 | )
61 |
62 | export default ResizeHandler
63 |
--------------------------------------------------------------------------------
/src/layouts/window/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export { default as useManager } from './useManager'
2 | export { default as useWindow, Resize } from './useWindow'
3 |
--------------------------------------------------------------------------------
/src/layouts/window/hooks/useManager.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | import { useAnimation } from 'framer-motion'
4 | import { expo } from '@services/ease'
5 |
6 | import { useApps } from '@stores/apps'
7 | import type { ActiveApp } from '@stores/apps'
8 |
9 | const useManager = (
10 | app: ActiveApp,
11 | {
12 | position,
13 | size
14 | }: {
15 | position: [number, number]
16 | size: [number, number]
17 | }
18 | ) => {
19 | const [, dispatch] = useApps()
20 | const animation = useAnimation()
21 |
22 | const [isMaximize, updateMaximize] = useState(false)
23 | const [isMinimize, updateMinimize] = useState(false)
24 |
25 | useEffect(() => {
26 | requestAnimationFrame(() => {
27 | requestAnimationFrame(() => {
28 | animation.start({
29 | opacity: 1,
30 | scale: 1,
31 | transition: {
32 | duration: 0.24,
33 | ease: expo.in
34 | }
35 | })
36 | })
37 | })
38 | }, [])
39 |
40 | useEffect(() => {
41 | if (!isMaximize)
42 | animation.set({
43 | top: position[1],
44 | left: position[0],
45 | width: size[0],
46 | height: size[1]
47 | })
48 | }, [size, position, isMaximize])
49 |
50 | const maximize = () => {
51 | if (isMaximize) {
52 | animation.start({
53 | top: position[1],
54 | left: position[0],
55 | width: size[0],
56 | height: size[1],
57 | borderRadius: '0.5em',
58 | borderWidth: 1,
59 | transition: {
60 | duration: 0.24,
61 | ease: expo.inOut
62 | }
63 | })
64 | return updateMaximize(false)
65 | }
66 |
67 | animation.start({
68 | width: '100%',
69 | height: 'calc(100vh - 48px)',
70 | top: 0,
71 | left: 0,
72 | borderRadius: 0,
73 | borderWidth: 0,
74 | transition: {
75 | duration: 0.24,
76 | ease: expo.inOut
77 | }
78 | })
79 |
80 | updateMaximize(true)
81 | }
82 |
83 | const minimize = () => {
84 | if (isMinimize) {
85 | animation.start({
86 | top: position[1],
87 | left: position[0],
88 | width: size[0],
89 | height: size[1],
90 | transition: {
91 | duration: 0.24,
92 | ease: expo.inOut
93 | }
94 | })
95 | return updateMinimize(false)
96 | }
97 |
98 | animation.start({
99 | opacity: 0,
100 | scale: 0.75,
101 | transition: {
102 | duration: 0.16
103 | }
104 | })
105 |
106 | updateMinimize(true)
107 | }
108 |
109 | const close = () => {
110 | dispatch({
111 | type: 'close',
112 | id: app.id
113 | })
114 | }
115 |
116 | const prioritize = () => {
117 | dispatch({
118 | type: 'prioritize',
119 | id: app.id
120 | })
121 | }
122 |
123 | return {
124 | isMaximize,
125 | isMinimize,
126 | prioritize,
127 | maximize,
128 | minimize,
129 | close,
130 | animation
131 | }
132 | }
133 |
134 | export default useManager
135 |
--------------------------------------------------------------------------------
/src/layouts/window/hooks/useWindow.ts:
--------------------------------------------------------------------------------
1 | import {
2 | useEffect,
3 | useReducer,
4 | useRef,
5 | useState
6 | } from 'react'
7 | import type { Reducer } from 'react'
8 |
9 | const minSize: [number, number] = [360, 240]
10 | const initSize: [number, number] = [720, 520]
11 |
12 | export enum Resize {
13 | none,
14 | top,
15 | left,
16 | bottom,
17 | right,
18 | topLeft,
19 | topRight,
20 | bottomLeft,
21 | bottomRight
22 | }
23 |
24 | const useWindow = () => {
25 | const [size, updateSize] = useReducer<
26 | Reducer<[number, number], [number, number]>
27 | >((v, arg) => {
28 | const width = v[0] + arg[0]
29 | const height = v[1] + arg[1]
30 |
31 | return [
32 | width >= minSize[0] ? width : minSize[0],
33 | height >= minSize[1] ? height : minSize[1]
34 | ]
35 | }, initSize)
36 |
37 | const [position, updatePosition] = useReducer<
38 | Reducer<[number, number], [number, number]>
39 | >(
40 | (v, arg) => {
41 | let x = v[0] + arg[0]
42 | let y = v[1] + arg[1]
43 |
44 | if (x < 0 || x > document.body.offsetWidth - size[0]) x = v[0]
45 | if (y < 0 || y > document.body.offsetHeight - size[1] - 48) y = v[1]
46 |
47 | return [x, y]
48 | },
49 | [0, 0]
50 | )
51 |
52 | const previousDrag = useRef(null)
53 |
54 | useEffect(() => {
55 | updatePosition([
56 | (document.body.offsetWidth - initSize[0]) / 2,
57 | (document.body.offsetHeight - initSize[1]) / 2 - 48
58 | ])
59 | }, [])
60 |
61 | const [actioning, updateAction] = useState(false)
62 |
63 | const drag = useRef(false)
64 | const resize = useRef(Resize.none)
65 |
66 | const startDrag = () => {
67 | drag.current = true
68 | updateAction(true)
69 | }
70 |
71 | const startResize = (action: Resize) => () => {
72 | resize.current = action
73 | updateAction(true)
74 | }
75 |
76 | const convertTouchToMove =
77 | (callback: (event: { movementX: number; movementY: number }) => void) =>
78 | (event: TouchEvent) => {
79 | const { pageX, pageY } = event.changedTouches[0]
80 |
81 | if (previousDrag.current) {
82 | const [previousX, previousY] = previousDrag.current
83 | const [movementX, movementY] = [
84 | pageX - previousX,
85 | pageY - previousY
86 | ]
87 |
88 | // ! Unsafe: We only extract movementX and Y here, so no worry
89 | callback({
90 | movementX,
91 | movementY
92 | })
93 | }
94 |
95 | previousDrag.current = [pageX, pageY]
96 | }
97 |
98 | useEffect(() => {
99 | const handleMove = (event: MouseEvent) => {
100 | if (drag.current)
101 | updatePosition([
102 | position[0] + event.movementX,
103 | position[1] + event.movementY
104 | ])
105 | }
106 |
107 | const handleResize = (event: MouseEvent) => {
108 | if (resize.current === Resize.none) return
109 |
110 | const { movementX: x, movementY: y } = event
111 |
112 | switch (resize.current) {
113 | case Resize.top:
114 | updateSize([0, y * -1])
115 | updatePosition([0, y])
116 | break
117 |
118 | case Resize.bottom:
119 | updateSize([0, y * 1])
120 | break
121 |
122 | case Resize.left:
123 | updateSize([x * -1, 0])
124 | updatePosition([x, 0])
125 | break
126 |
127 | case Resize.right:
128 | updateSize([x, 0])
129 | break
130 |
131 | case Resize.topLeft:
132 | updateSize([x * -1, y * -1])
133 | updatePosition([x, y])
134 | break
135 |
136 | case Resize.topRight:
137 | updateSize([x, y * -1])
138 | updatePosition([0, y])
139 | break
140 |
141 | case Resize.bottomLeft:
142 | updateSize([x * -1, y])
143 | updatePosition([x, 0])
144 | break
145 |
146 | case Resize.bottomRight:
147 | updateSize([x, y])
148 | break
149 | }
150 | }
151 |
152 | document.addEventListener('mousemove', handleMove, {
153 | passive: true
154 | })
155 |
156 | const handleTouch = convertTouchToMove((movement) => {
157 | // ! Unsafe: We only extract movementX and Y here, so no worry
158 | handleMove(movement as any)
159 | handleResize(movement as any)
160 | })
161 |
162 | document.addEventListener('touchmove', handleTouch, {
163 | passive: true
164 | })
165 |
166 | document.addEventListener('mousemove', handleResize, {
167 | passive: true
168 | })
169 |
170 | const stopHandler = () => {
171 | drag.current = false
172 | resize.current = Resize.none
173 | previousDrag.current = null
174 | }
175 |
176 | const stopEvents = ['mouseup', 'mouseleave', 'touchcanel', 'touchend']
177 |
178 | stopEvents.forEach((event) => {
179 | document.addEventListener(event, stopHandler, {
180 | passive: true
181 | })
182 | })
183 |
184 | return () => {
185 | document.removeEventListener('mousemove', handleMove)
186 | document.removeEventListener('mousemove', handleResize)
187 | document.removeEventListener('touchmove', handleTouch)
188 |
189 | stopEvents.forEach((event) => {
190 | document.removeEventListener(event, stopHandler)
191 | })
192 | }
193 | }, [])
194 |
195 | return {
196 | size,
197 | position,
198 | actioning,
199 | startDrag,
200 | startResize
201 | }
202 | }
203 |
204 | export default useWindow
205 |
--------------------------------------------------------------------------------
/src/layouts/window/index.tsx:
--------------------------------------------------------------------------------
1 | import { Suspense } from 'react'
2 |
3 | import { motion } from 'framer-motion'
4 |
5 | import { Minimize2, Minus, Square, X } from 'react-feather'
6 | import ResizeHandler from './components/resizeHandler'
7 |
8 | import { useManager, useWindow } from './hooks'
9 |
10 | import type { WindowComponent } from './types'
11 |
12 | import style from './window.module.sass'
13 |
14 | const Window: WindowComponent = ({
15 | app,
16 | app: { name, icon, app: Applet = null, className = '' },
17 | }) => {
18 | const { position, size, actioning, startDrag, startResize } = useWindow()
19 | const { minimize, maximize, close, prioritize, isMaximize, animation } =
20 | useManager(app, {
21 | position,
22 | size
23 | })
24 |
25 | const handleStartDrag = () => {
26 | prioritize()
27 | startDrag()
28 | }
29 |
30 | return (
31 |
51 | {actioning && (
52 |
62 | )}
63 |
64 |
65 |
71 |
72 |
77 | {name}
78 |
79 |
80 |
89 |
105 |
111 |
112 |
113 |
114 | {Applet && (
115 | }>
116 |
117 |
118 | )}
119 |
120 |
121 | )
122 | }
123 |
124 | export default Window
125 |
--------------------------------------------------------------------------------
/src/layouts/window/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent } from "react"
2 | import type { ActiveApp } from "@stores/apps"
3 |
4 | export interface WindowProps {
5 | app: ActiveApp
6 | className?: string
7 | }
8 |
9 | export type WindowComponent = FunctionComponent
--------------------------------------------------------------------------------
/src/layouts/window/window.module.sass:
--------------------------------------------------------------------------------
1 | .window
2 | box-shadow: 0 8px 48px rgba(0,0,0,.15)
3 | will-change: top, left, bottom, right, width, height, transform, border-radius
4 |
5 | .icon
6 | user-select: none
7 | -webkit-user-drag: none
8 |
9 | .drag-horizontal
10 | cursor: ew-resize
11 |
12 | .drag-vertical
13 | cursor: ns-resize
14 |
15 | .drag-left-slash
16 | cursor: nwse-resize
17 |
18 | .drag-right-slash
19 | cursor: nesw-resize
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 |
4 | import App from './App'
5 |
6 | import './styles/tailwind.css'
7 | import './styles/index.sass'
8 | import './styles/app.sass'
9 |
10 | ReactDOM.render(
11 |
12 |
13 | ,
14 | document.getElementById('root')
15 | )
16 |
--------------------------------------------------------------------------------
/src/modules/context-menu/components/context-balloon/index.tsx:
--------------------------------------------------------------------------------
1 | import { Fragment, useState, useReducer, useEffect, useRef } from 'react'
2 |
3 | import { motion, useAnimation } from 'framer-motion'
4 |
5 | import { getContextMenuHeight } from '@modules/context-menu/services'
6 | import { expo } from '@services/ease'
7 |
8 | import type { ContextBalloonComponent } from './types'
9 |
10 | import styles from '@modules/context-menu/context.module.sass'
11 |
12 | const ContextBalloon: ContextBalloonComponent = ({
13 | position,
14 | contexts = [],
15 | index,
16 | className = '',
17 | bottomUp = false
18 | }) => {
19 | const height = getContextMenuHeight(contexts)
20 |
21 | const [left, updateLeft] = useState(0)
22 | const [isInit, init] = useReducer(() => true, false)
23 | const menu = useRef(null)
24 |
25 | const animate = useAnimation()
26 |
27 | useEffect(() => {
28 | const width = menu.current!.clientWidth
29 | const previousWidth =
30 | index <= 0
31 | ? undefined
32 | : document.getElementById(`context-${index - 1}`)!.clientWidth + width - 8
33 |
34 | if (position.left && position.left > document.body.clientWidth - width)
35 | updateLeft(position.left - (previousWidth ?? width))
36 | else updateLeft(position.left ?? 0)
37 |
38 | init()
39 | animate.start({
40 | height,
41 | marginTop: 0,
42 | transition: {
43 | ease: expo.in,
44 | duration: 0.48
45 | }
46 | })
47 | }, [])
48 |
49 | const marginTop = bottomUp ? height : 0
50 |
51 | return (
52 |
79 | {contexts.map((row, index) => (
80 |
81 | {row.map((context, index) => (
82 | {context}
83 | ))}
84 | {index !== contexts.length - 1 && (
85 |
86 | )}
87 |
88 | ))}
89 |
90 | )
91 | }
92 |
93 | export default ContextBalloon
94 |
--------------------------------------------------------------------------------
/src/modules/context-menu/components/context-balloon/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent } from 'react'
2 |
3 | export interface ContextBalloonProps {
4 | index: number
5 | className?: string
6 | contexts?: JSX.Element[][]
7 | bottomUp?: boolean
8 | position: {
9 | top?: number
10 | bottom?: number
11 | left?: number
12 | right?: number
13 | }
14 | }
15 |
16 | export type ContextBalloonComponent = FunctionComponent
--------------------------------------------------------------------------------
/src/modules/context-menu/components/context/index.tsx:
--------------------------------------------------------------------------------
1 | import { DOMAttributes, useEffect, useRef } from 'react'
2 |
3 | import { useContextMenu } from '@stores/context-menu'
4 |
5 | import { ChevronRight } from 'react-feather'
6 |
7 | import type { ContextComponent } from './types'
8 |
9 | import styles from '@modules/context-menu/context.module.sass'
10 |
11 | export const height = 32
12 |
13 | const Context: ContextComponent = ({
14 | icon,
15 | title,
16 | suffix,
17 | menu,
18 | onClick,
19 | ...props
20 | }) => {
21 | const [context, dispatch] = useContextMenu()
22 |
23 | const button = useRef(null)
24 | const appended = useRef(false)
25 | const justAppended = useRef(false)
26 |
27 | useEffect(() => {
28 | const main = () => {
29 | if (justAppended.current) return (justAppended.current = false)
30 |
31 | appended.current = false
32 | }
33 |
34 | main()
35 | }, [context])
36 |
37 | const showContext = () => {
38 | if (appended.current || !context.length) return
39 |
40 | const current = button.current!
41 | const id = current.parentElement?.id!
42 | const index = +id?.replace('context-', '')
43 |
44 | appended.current = true
45 | justAppended.current = true
46 |
47 | dispatch({
48 | type: 'append',
49 | index,
50 | contexts: menu || [],
51 | position: {
52 | top: current.getBoundingClientRect().top + 4
53 | }
54 | })
55 | }
56 |
57 | const handleClick: DOMAttributes['onClick'] = (
58 | event
59 | ) => {
60 | if(!menu)
61 | dispatch({
62 | type: 'clear'
63 | })
64 |
65 | if (onClick) onClick(event)
66 | }
67 |
68 | return (
69 |
98 | )
99 | }
100 |
101 | export default Context
102 |
--------------------------------------------------------------------------------
/src/modules/context-menu/components/context/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent, ButtonHTMLAttributes } from 'react'
2 |
3 | export interface ContextProps extends Omit, 'title'> {
4 | icon?: JSX.Element | string
5 | title: string
6 | suffix?: JSX.Element | string
7 | menu?: JSX.Element[][]
8 | dense?: boolean
9 | }
10 |
11 | export type ContextComponent = FunctionComponent
12 |
--------------------------------------------------------------------------------
/src/modules/context-menu/components/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Context, height as contextHeight } from './context'
2 | export { default as ContextBalloon } from './context-balloon'
3 |
--------------------------------------------------------------------------------
/src/modules/context-menu/context.module.sass:
--------------------------------------------------------------------------------
1 | .context
2 | @apply mx-auto transition-colors
3 | width: calc(100% - .5em)
4 | height: 2em
5 |
6 | &:hover,
7 | &:focus
8 | background-color: rgba(0,0,0,.05)
9 |
10 | .balloon
11 | will-change: height
12 | box-shadow: 0 2px 8px rgba(0,0,0,.15)
--------------------------------------------------------------------------------
/src/modules/context-menu/index.tsx:
--------------------------------------------------------------------------------
1 | import { AnimatePresence } from 'framer-motion'
2 |
3 | import { useContextMenu } from '@stores/context-menu'
4 |
5 | import { ContextBalloon } from './components'
6 |
7 | const ContextMenu = () => {
8 | const [contextMenus, dispatchContextMenu] = useContextMenu()
9 |
10 | const dismissContextMenu = () => {
11 | dispatchContextMenu({
12 | type: 'clear'
13 | })
14 | }
15 |
16 | return (
17 | <>
18 |
19 | {contextMenus.length && (
20 |
25 | )}
26 |
27 |
28 | {contextMenus.length && (
29 | <>
30 | {contextMenus.map(({ created, ...props }, index) => (
31 |
36 | ))}
37 | >
38 | )}
39 |
40 | >
41 | )
42 | }
43 |
44 | export { Context, ContextBalloon } from './components'
45 | export default ContextMenu
46 |
--------------------------------------------------------------------------------
/src/modules/context-menu/services/index.ts:
--------------------------------------------------------------------------------
1 | import { contextHeight } from '@modules/context-menu/components'
2 |
3 | const em = 16
4 |
5 | export const getContextMenuHeight = (contexts: unknown[][]) =>
6 | // each slot height
7 | contexts.flat(2).length * contextHeight +
8 | // divider height
9 | contexts.length * em * 0.25 * 2 +
10 | // padding top-bottom
11 | em * 0.25 * 2 +
12 | // grid gap
13 | contexts.length * em * 0.125 * 2
14 |
--------------------------------------------------------------------------------
/src/modules/sidebar/components/index.ts:
--------------------------------------------------------------------------------
1 | export { Calendar } from './organisms'
--------------------------------------------------------------------------------
/src/modules/sidebar/components/organisms/calendar/index.tsx:
--------------------------------------------------------------------------------
1 | import dayjs from 'dayjs'
2 |
3 | import { Triangle } from 'react-feather'
4 |
5 | import styles from '@modules/sidebar/sidebar.module.sass'
6 |
7 | const Sidebar = () => {
8 | const date = dayjs()
9 | const firstDay = Array(date.startOf('month').day()).fill(null)
10 | const lastDayOfLastMonth = date.subtract(1, 'month').endOf('month').date()
11 | const currentDay = date.date()
12 |
13 | return (
14 |
15 |
42 |
43 | {['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'].map((day) => (
44 |
45 | {day}
46 |
47 | ))}
48 | {firstDay.map((_, index) => (
49 |
55 | ))}
56 | {Array(date.daysInMonth())
57 | .fill(null)
58 | .map((_, index) => (
59 |
69 | ))}
70 | {Array(6 - date.endOf('month').day())
71 | .fill(null)
72 | .map((_, index) => (
73 |
79 | ))}
80 |
81 |
82 | )
83 | }
84 |
85 | export default Sidebar
86 |
--------------------------------------------------------------------------------
/src/modules/sidebar/components/organisms/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Calendar } from './calendar'
--------------------------------------------------------------------------------
/src/modules/sidebar/index.tsx:
--------------------------------------------------------------------------------
1 | import { AnimatePresence, motion } from 'framer-motion'
2 |
3 | import { useSidebar } from './stores'
4 |
5 | import { Calendar } from './components'
6 |
7 | import { expo } from '@services/ease'
8 |
9 | const width = 340
10 |
11 | const Sidebar = () => {
12 | const [sidebar, updateSidebar] = useSidebar()
13 |
14 | const closeSidebar = () => {
15 | updateSidebar(false)
16 | }
17 |
18 | return (
19 | <>
20 | {sidebar && (
21 |
25 | )}
26 |
27 |
28 | {sidebar && (
29 |
54 |
55 |
56 | )}
57 |
58 | >
59 | )
60 | }
61 |
62 | export default Sidebar
63 |
--------------------------------------------------------------------------------
/src/modules/sidebar/sidebar.module.sass:
--------------------------------------------------------------------------------
1 | .pane
2 | @apply w-full p-2 rounded-lg
3 |
4 | .date
5 | @apply flex justify-center items-center w-[40px] h-[40px] text-sm text-center m-auto border border-transparent rounded-full transition-colors
6 | @apply ring ring-1 ring-blue-400
7 |
8 | &:hover
9 | background-color: rgba(0,0,0,.05)
10 |
11 | &:focus
12 | @apply border-blue-500
13 |
14 | &.-active
15 | @apply bg-blue-500 text-white
--------------------------------------------------------------------------------
/src/modules/sidebar/stores/index.ts:
--------------------------------------------------------------------------------
1 | import { atom, useAtom } from 'jotai'
2 |
3 | export const sidebarAtom = atom(false)
4 | export const useSidebar = () => useAtom(sidebarAtom)
5 |
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/card/card.module.sass:
--------------------------------------------------------------------------------
1 | .card
2 | @apply transition-colors
3 | background-color: rgba(255,255,255,.6)
4 |
5 | &:hover,
6 | &:focus
7 | background-color: rgba(255,255,255,.3)
8 |
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/card/index.tsx:
--------------------------------------------------------------------------------
1 | import type { CardComponent } from './types'
2 |
3 | import styles from './card.module.sass'
4 |
5 | const Card: CardComponent = ({ name, icon }) => {
6 | return (
7 |
11 | )
12 | }
13 |
14 | export default Card
15 |
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/card/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent } from 'react'
2 |
3 | export interface CardProps {
4 | name: string
5 | icon: string
6 | }
7 |
8 | export type CardComponent = FunctionComponent
9 |
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Card } from './card'
2 | export { default as Li } from './li'
3 | export { default as Overlay } from './overlay'
4 | export { default as Pin } from './pin'
5 | export { default as Recommended } from './recommended'
6 | export { default as Resetter } from './resetter'
7 | export { default as Selection } from './selection'
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/li/index.tsx:
--------------------------------------------------------------------------------
1 | import type { LiComponent } from './types'
2 |
3 | const Li: LiComponent = ({ icon, title, detail }) => (
4 |
18 | )
19 |
20 | export default Li
21 |
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/li/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent } from 'react'
2 |
3 | export interface LiProps {
4 | icon?: JSX.Element | string
5 | title: JSX.Element | string
6 | detail?: JSX.Element | string
7 | }
8 |
9 | export type LiComponent = FunctionComponent
10 |
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/overlay/index.tsx:
--------------------------------------------------------------------------------
1 | import { useStartMenuVisibility } from '@stores/start'
2 |
3 | const Overlay = ({ fullscreen = false }) => {
4 | const [, updateStartMenu] = useStartMenuVisibility()
5 |
6 | const dismiss = () => {
7 | updateStartMenu(false)
8 | }
9 |
10 | if (fullscreen)
11 | return (
12 |
16 | )
17 |
18 | return
19 | }
20 |
21 | export default Overlay
22 |
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/pin/index.tsx:
--------------------------------------------------------------------------------
1 | import type { PinComponent } from './types'
2 |
3 | const Pin: PinComponent = ({ icon, name }) => (
4 |
8 | )
9 |
10 | export default Pin
11 |
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/pin/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent } from 'react'
2 |
3 | export interface PinProps {
4 | name: string
5 | icon: string
6 | }
7 |
8 | export type PinComponent = FunctionComponent
9 |
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/recommended/index.tsx:
--------------------------------------------------------------------------------
1 | import type { RecommendedComponent } from './types'
2 |
3 | const Recommended: RecommendedComponent = ({ icon, name, subTitle }) => (
4 |
11 | )
12 |
13 | export default Recommended
14 |
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/recommended/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent } from 'react'
2 |
3 | export interface RecommendedProps {
4 | name: string
5 | subTitle?: string
6 | icon: string
7 | }
8 |
9 | export type RecommendedComponent = FunctionComponent
10 |
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/resetter/index.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 |
3 | import { useStartMenuType } from '@stores/start'
4 | import { useSearch, useOverviewPage } from '@modules/start/stores'
5 |
6 | import { motion } from 'framer-motion'
7 |
8 | const Resetter = () => {
9 | const [, updateType] = useStartMenuType()
10 | const [, updatePage] = useOverviewPage()
11 | const [, updateSearch] = useSearch()
12 |
13 | useEffect(() => {
14 | return () => {
15 | updateType('overview')
16 | updatePage('quick')
17 | updateSearch('')
18 | }
19 | }, [])
20 |
21 | return (
22 |
32 | )
33 | }
34 |
35 | export default Resetter
36 |
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/selection/index.tsx:
--------------------------------------------------------------------------------
1 | import type { StartMenuSelectionComponent } from './types'
2 |
3 | import styles from './selection.module.sass'
4 |
5 | const Selection: StartMenuSelectionComponent = ({
6 | children,
7 | className,
8 | active,
9 | activeColor = '#007aff',
10 | ...props
11 | }) => {
12 | return (
13 |
29 | )
30 | }
31 |
32 | export default Selection
33 |
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/selection/selection.module.sass:
--------------------------------------------------------------------------------
1 | .button
2 | @apply text-gray-500 transition-colors
3 |
4 | &:hover,
5 | &:focus,
6 | .-active
7 | @apply text-black
8 |
9 | .selection
10 | @apply opacity-0
11 | width: 0
12 | height: 4px
13 | transition: width .26s ease-out, opacity .12s ease-out
14 |
15 | &.-active
16 | @apply opacity-100
17 | width: 16px
--------------------------------------------------------------------------------
/src/modules/start/components/atoms/selection/types.ts:
--------------------------------------------------------------------------------
1 | import type { ButtonHTMLAttributes, FunctionComponent } from 'react'
2 |
3 | export interface StartMenuSelectionProps extends ButtonHTMLAttributes {
4 | activeColor?: string
5 | className?: string
6 | active?: boolean
7 | }
8 |
9 | export type StartMenuSelectionComponent = FunctionComponent
10 |
--------------------------------------------------------------------------------
/src/modules/start/components/index.ts:
--------------------------------------------------------------------------------
1 | export {
2 | Card,
3 | Li,
4 | Overlay,
5 | Pin,
6 | Recommended,
7 | Resetter,
8 | Selection
9 | } from './atoms'
10 | export {
11 | AllApps,
12 | KeyboardListener,
13 | RecommendedPage,
14 | QuickOverview,
15 | SearchBar,
16 | SearchOptions
17 | } from './molecules'
18 | export { Overview, Search } from './organisms'
19 |
--------------------------------------------------------------------------------
/src/modules/start/components/molecules/all-apps/index.tsx:
--------------------------------------------------------------------------------
1 | import { useOverviewPage } from '@modules/start/stores'
2 |
3 | import { Button } from '@atoms'
4 | import { Li } from '@modules/start/components'
5 |
6 | import { ChevronLeft } from 'react-feather'
7 |
8 | import styles from '@modules/start/styles.module.sass'
9 |
10 | const AllApps = () => {
11 | const [, updatePage] = useOverviewPage()
12 |
13 | const back = () => {
14 | updatePage('quick')
15 | }
16 |
17 | return (
18 | <>
19 |
20 | Recommended
21 |
28 |
29 |
30 | - F} />
31 |
32 |
33 | >
34 | )
35 | }
36 |
37 | export default AllApps
38 |
--------------------------------------------------------------------------------
/src/modules/start/components/molecules/index.ts:
--------------------------------------------------------------------------------
1 | export { default as AllApps } from './all-apps'
2 | export { default as KeyboardListener } from './keyboard-listener'
3 | export { default as QuickOverview } from './quick-view'
4 | export { default as RecommendedPage } from './recommended'
5 | export { default as SearchBar } from './search-bar'
6 | export { default as SearchOptions } from './search-options'
--------------------------------------------------------------------------------
/src/modules/start/components/molecules/keyboard-listener/index.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react'
2 |
3 | import { useToggleStartMenu } from '@stores/start'
4 | import { useContextMenu } from '@stores/context-menu'
5 |
6 | const KeyboardListener = () => {
7 | const [, dispatchContext] = useContextMenu()
8 | const [, toggleStartMenu] = useToggleStartMenu()
9 |
10 | const pressing = useRef([])
11 |
12 | useEffect(() => {
13 | window.addEventListener(
14 | 'keydown',
15 | (event) => {
16 | if (!pressing.current.includes(event.code))
17 | pressing.current.push(event.code)
18 | },
19 | {
20 | passive: true
21 | }
22 | )
23 |
24 | window.addEventListener('keyup', (event) => {
25 | if (
26 | (pressing.current.length === 1 &&
27 | pressing.current.includes('MetaLeft')) ||
28 | pressing.current.includes('MetaRight')
29 | ) {
30 | event.preventDefault()
31 | toggleStartMenu()
32 | dispatchContext({
33 | type: 'clear'
34 | })
35 | }
36 |
37 | pressing.current = []
38 | })
39 | }, [])
40 |
41 | return null
42 | }
43 |
44 | export default KeyboardListener
45 |
--------------------------------------------------------------------------------
/src/modules/start/components/molecules/quick-view/index.tsx:
--------------------------------------------------------------------------------
1 | import { useOverviewPage } from '@modules/start/stores'
2 |
3 | import { ChevronRight, Power } from 'react-feather'
4 |
5 | import { Button, Tooltip } from '@atoms'
6 | import { Pin, Recommended } from '@modules/start/components'
7 |
8 | import styles from '@modules/start/styles.module.sass'
9 |
10 | const QuickView = () => {
11 | const [, updatePage] = useOverviewPage()
12 |
13 | const toAllApps = () => {
14 | updatePage('apps')
15 | }
16 |
17 | const toRecommended = () => {
18 | updatePage('recommended')
19 | }
20 |
21 | return (
22 | <>
23 |
24 |
25 | Pinned
26 |
33 |
34 |
38 |
39 |
40 |
41 |
42 | Recommended
43 |
50 |
51 |
63 |
64 | >
65 | )
66 | }
67 |
68 | export default QuickView
69 |
--------------------------------------------------------------------------------
/src/modules/start/components/molecules/recommended/index.tsx:
--------------------------------------------------------------------------------
1 | import { useOverviewPage } from '@modules/start/stores'
2 |
3 | import { Button } from '@atoms'
4 | import { Li } from '@modules/start/components'
5 |
6 | import { ChevronLeft } from 'react-feather'
7 |
8 | import styles from '@modules/start/styles.module.sass'
9 |
10 | const Recommended = () => {
11 | const [, updatePage] = useOverviewPage()
12 |
13 | const back = () => {
14 | updatePage('quick')
15 | }
16 |
17 | return (
18 | <>
19 |
20 | Recommended
21 |
28 |
29 |
30 |
35 |
36 |
37 | >
38 | )
39 | }
40 |
41 | export default Recommended
42 |
--------------------------------------------------------------------------------
/src/modules/start/components/molecules/search-bar/index.tsx:
--------------------------------------------------------------------------------
1 | import type { DOMAttributes } from 'react'
2 |
3 | import { Search } from 'react-feather'
4 |
5 | import { TextInput } from '@components'
6 |
7 | import { useSearch } from '@modules/start/stores'
8 |
9 | const SearchBar = ({ className = "", disabled = false }) => {
10 | const [search, updateSearch] = useSearch()
11 |
12 | const preventDefault: DOMAttributes['onSubmit'] = (
13 | event
14 | ) => {
15 | event.preventDefault()
16 | }
17 |
18 | const broadcastSearch: DOMAttributes['onChange'] = (
19 | event
20 | ) => {
21 | updateSearch(event.currentTarget.value)
22 | }
23 |
24 | return (
25 |
35 | )
36 | }
37 |
38 | export default SearchBar
39 |
--------------------------------------------------------------------------------
/src/modules/start/components/molecules/search-options/index.tsx:
--------------------------------------------------------------------------------
1 | import { useCallback } from 'react'
2 | import type { DOMAttributes } from 'react'
3 |
4 | import { useContextMenu } from '@stores/context-menu'
5 |
6 | import { ChevronDown } from 'react-feather'
7 |
8 | import { Context } from '@modules/context-menu'
9 |
10 | import { Selection } from '@modules/start/components'
11 | import {
12 | useSearch,
13 | searchTypes,
14 | displayedShortcut
15 | } from '@modules/start/stores'
16 | import type { SearchType } from '@modules/start/stores'
17 |
18 | const searchTypeRegEx = RegExp(`^(${searchTypes.join('|')})`)
19 |
20 | const SearchOptions = () => {
21 | const [, updateContextMenu] = useContextMenu()
22 | const [searchValue, updateSearch] = useSearch()
23 | const search = searchValue.toLowerCase()
24 |
25 | const replaceSearchType = useCallback((type: SearchType) => () => {
26 | let value = searchValue.replace(searchTypeRegEx, type)
27 | if (!value.startsWith(`${type}:`)) value = `${type}: ${value}`
28 |
29 | updateSearch(value)
30 | }, [])
31 |
32 | const removeSearchType = useCallback(() => {
33 | let value = searchValue.replace(searchTypeRegEx, '').replace(/^: /, '')
34 |
35 | updateSearch(value)
36 | }, [])
37 |
38 | const showContext: DOMAttributes['onClick'] = useCallback((
39 | event
40 | ) => {
41 | const element = event.currentTarget
42 | const { top, left } = element.getBoundingClientRect()
43 |
44 | updateContextMenu({
45 | type: 'append',
46 | index: 0,
47 | position: {
48 | top: top + element.clientHeight,
49 | left
50 | },
51 | contexts: [
52 | [
53 | ,
57 | ,
61 | ,
65 |
69 | ]
70 | ]
71 | })
72 | }, [])
73 |
74 | const shortcut = searchTypes.find(
75 | (type) =>
76 | search.startsWith(`${type}:`) && !displayedShortcut.includes(type)
77 | )
78 |
79 | return (
80 |
81 | search.startsWith(`${type}:`))
84 | }
85 | onClick={removeSearchType}
86 | >
87 | All
88 |
89 | {displayedShortcut.map((type) => (
90 |
95 | {type}
96 |
97 | ))}
98 | {shortcut && {shortcut}}
99 |
100 | More
101 |
102 |
103 | )
104 | }
105 |
106 | export default SearchOptions
107 |
--------------------------------------------------------------------------------
/src/modules/start/components/organisms/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Overview } from './overview'
2 | export { default as Search } from './search'
3 |
--------------------------------------------------------------------------------
/src/modules/start/components/organisms/overview/index.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 |
3 | import { useStartMenuType } from '@stores/start'
4 |
5 | import { useOverviewPage } from '@modules/start/stores'
6 | import { AnimatePage, StartLayout } from '@modules/start/layouts'
7 | import {
8 | AllApps,
9 | QuickOverview,
10 | RecommendedPage,
11 | SearchBar
12 | } from '@modules/start/components'
13 |
14 | import { Power } from 'react-feather'
15 |
16 | import { Tooltip } from '@atoms'
17 |
18 | const Overview = () => {
19 | const [, updateStartMenuType] = useStartMenuType()
20 | const [page] = useOverviewPage()
21 |
22 | const toggleSearchMenu = () => {
23 | updateStartMenuType('search')
24 | }
25 |
26 | useEffect(() => {
27 | const handleSearch = (event: KeyboardEvent) => {
28 | if (
29 | event.metaKey ||
30 | event.shiftKey ||
31 | event.altKey ||
32 | event.ctrlKey ||
33 | event.code === 'Tab' ||
34 | event.code.startsWith('Arrow')
35 | )
36 | return
37 |
38 | toggleSearchMenu()
39 | }
40 |
41 | window.addEventListener('keydown', handleSearch, {
42 | passive: true
43 | })
44 |
45 | return () => {
46 | window.removeEventListener('keydown', handleSearch)
47 | }
48 | }, [])
49 |
50 | return (
51 |
52 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
103 |
104 | )
105 | }
106 |
107 | export default Overview
108 |
--------------------------------------------------------------------------------
/src/modules/start/components/organisms/search/index.tsx:
--------------------------------------------------------------------------------
1 | import { MoreHorizontal } from 'react-feather'
2 |
3 | import { StartLayout } from '@modules/start/layouts'
4 | import { Card, Li, SearchBar, SearchOptions } from '@modules/start/components'
5 |
6 | import styles from '@modules/start/styles.module.sass'
7 |
8 | const Search = () => (
9 |
10 |
11 |
12 |
20 |
21 | Top Apps
22 |
27 |
28 | Recent
29 |
30 |
31 |
32 |
33 | )
34 |
35 | export default Search
36 |
--------------------------------------------------------------------------------
/src/modules/start/index.tsx:
--------------------------------------------------------------------------------
1 | import { AnimatePresence } from 'framer-motion'
2 |
3 | import { useStartMenuType, useStartMenuVisibility } from '@stores/start'
4 |
5 | import {
6 | KeyboardListener,
7 | Overview,
8 | Overlay,
9 | Search,
10 | Resetter
11 | } from './components'
12 |
13 | const StartMenu = () => {
14 | const [visibility] = useStartMenuVisibility()
15 | const [type] = useStartMenuType()
16 |
17 | return (
18 | <>
19 |
20 | {visibility &&
21 | (type === 'overview' ? : )}
22 |
23 |
24 | {visibility && }
25 | {visibility && }
26 |
27 | >
28 | )
29 | }
30 |
31 | export default StartMenu
32 |
--------------------------------------------------------------------------------
/src/modules/start/layouts/animated-page/animate-page.module.sass:
--------------------------------------------------------------------------------
1 | .animate-page
2 | will-change: opacity, transform
--------------------------------------------------------------------------------
/src/modules/start/layouts/animated-page/index.tsx:
--------------------------------------------------------------------------------
1 | import { AnimatePresence, motion } from 'framer-motion'
2 |
3 | import { expo } from '@services/ease'
4 |
5 | import type { AnimatePageComponent } from './types'
6 |
7 | import styles from './animate-page.module.sass'
8 |
9 | const duration = 0.16
10 | const move = 24
11 |
12 | const animate = {
13 | start: {
14 | style: {
15 | translateX: -move,
16 | opacity: 0,
17 | position: 'absolute'
18 | },
19 | transition: {
20 | ease: expo.in,
21 | duration
22 | },
23 | animate: {
24 | position: 'relative',
25 | translateX: 0,
26 | opacity: 1,
27 | transition: {
28 | delay: duration / 2
29 | }
30 | },
31 | exit: {
32 | translateX: -move,
33 | opacity: 0
34 | }
35 | },
36 | end: {
37 | style: {
38 | translateX: move,
39 | opacity: 0
40 | },
41 | transition: {
42 | ease: expo.in,
43 | duration
44 | },
45 | animate: {
46 | translateX: 0,
47 | opacity: 1,
48 | transition: {
49 | delay: duration / 2
50 | }
51 | },
52 | exit: {
53 | translateX: move,
54 | opacity: 0
55 | }
56 | }
57 | } as const
58 |
59 | const AnimatePage: AnimatePageComponent = ({
60 | children,
61 | condition,
62 | start = false
63 | }) => (
64 |
65 | {condition && (
66 |
70 | {children}
71 |
72 | )}
73 |
74 | )
75 |
76 | export default AnimatePage
77 |
--------------------------------------------------------------------------------
/src/modules/start/layouts/animated-page/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent } from 'react'
2 |
3 | export interface AnimatePageProps {
4 | condition: boolean
5 | start?: boolean
6 | }
7 |
8 | export type AnimatePageComponent = FunctionComponent
9 |
--------------------------------------------------------------------------------
/src/modules/start/layouts/index.ts:
--------------------------------------------------------------------------------
1 | export { default as AnimatePage } from './animated-page'
2 | export { default as StartLayout } from './start'
--------------------------------------------------------------------------------
/src/modules/start/layouts/start/index.tsx:
--------------------------------------------------------------------------------
1 | import { motion } from 'framer-motion'
2 |
3 | import { Overlay } from '@modules/start/components'
4 | import styles from '@modules/start/styles.module.sass'
5 |
6 | import { expo } from '@services/ease'
7 |
8 | import type { StartLayoutComponent } from './types'
9 |
10 | const StartLayout: StartLayoutComponent = ({
11 | children,
12 | width = 640,
13 | className = ''
14 | }) => (
15 |
35 |
36 |
44 |
45 | )
46 |
47 | export default StartLayout
48 |
--------------------------------------------------------------------------------
/src/modules/start/layouts/start/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent } from 'react'
2 |
3 | export interface StartLayoutProps {
4 | width: number
5 | className?: string
6 | }
7 |
8 | export type StartLayoutComponent = FunctionComponent
9 |
--------------------------------------------------------------------------------
/src/modules/start/stores/index.ts:
--------------------------------------------------------------------------------
1 | export { overviewPageAtom, useOverviewPage } from './overview'
2 | export type { OverviewPage } from './overview'
3 |
4 | export { searchTypes, displayedShortcut, searchAtom, useSearch } from './search'
5 | export type { SearchType } from './search'
6 |
--------------------------------------------------------------------------------
/src/modules/start/stores/overview.ts:
--------------------------------------------------------------------------------
1 | import { atom, useAtom } from 'jotai'
2 |
3 | export type OverviewPage = 'quick' | 'apps' | 'recommended'
4 | export const overviewPageAtom = atom('quick')
5 | export const useOverviewPage = () => useAtom(overviewPageAtom)
6 |
--------------------------------------------------------------------------------
/src/modules/start/stores/search.ts:
--------------------------------------------------------------------------------
1 | import { atom, useAtom } from 'jotai'
2 |
3 | export type SearchType = 'all' | 'apps' | 'documents' | 'settings' | 'folders' | 'music' | 'photos' | 'videos'
4 | export const searchTypes: readonly SearchType[] = [
5 | 'apps',
6 | 'documents',
7 | 'settings',
8 | 'folders',
9 | 'music',
10 | 'photos',
11 | 'videos'
12 | ] as const
13 | export const displayedShortcut: readonly SearchType[] = [
14 | 'apps',
15 | 'documents',
16 | 'settings',
17 | ] as const
18 |
19 |
20 | export const searchAtom = atom('')
21 | export const useSearch = () => useAtom(searchAtom)
22 |
--------------------------------------------------------------------------------
/src/modules/start/styles.module.sass:
--------------------------------------------------------------------------------
1 | .start-menu
2 | height: 680px
3 | will-change: bottom
4 |
5 | .sub-title
6 | @apply text-sm text-gray-700 font-semibold cursor-default ml-8
7 |
8 | .heading
9 | @apply flex flex-row justify-between items-center w-full
--------------------------------------------------------------------------------
/src/modules/taskbar/components/atoms/index.ts:
--------------------------------------------------------------------------------
1 | export { default as TaskbarApp } from './taskbar-app'
2 | export { default as TaskbarItem } from './taskbar-item'
3 | export { default as WindowButton } from './windows-button'
--------------------------------------------------------------------------------
/src/modules/taskbar/components/atoms/taskbar-app/index.tsx:
--------------------------------------------------------------------------------
1 | import { useAppLauncher } from '@stores/apps'
2 |
3 | import { TaskbarItem } from '..'
4 |
5 | import type { TaskbarAppComponent } from './types'
6 |
7 | const TaskbarApp: TaskbarAppComponent = ({ app, onClick, ...props }) => {
8 | const launch = useAppLauncher(app)
9 |
10 | return (
11 |
12 |
17 |
18 | )
19 | }
20 |
21 | export default TaskbarApp
22 |
--------------------------------------------------------------------------------
/src/modules/taskbar/components/atoms/taskbar-app/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent, ButtonHTMLAttributes } from 'react'
2 |
3 | import type { App } from '@data/apps'
4 |
5 | export interface TaskbarAppProps
6 | extends ButtonHTMLAttributes {
7 | app: App
8 | }
9 |
10 | export type TaskbarAppComponent = FunctionComponent
11 |
--------------------------------------------------------------------------------
/src/modules/taskbar/components/atoms/taskbar-item/index.tsx:
--------------------------------------------------------------------------------
1 | import { Tooltip } from '@atoms'
2 |
3 | import styles from '@modules/taskbar/styles.module.sass'
4 |
5 | import type { TaskbarItemComponent } from './types'
6 |
7 | const TaskbarItem: TaskbarItemComponent = ({
8 | className,
9 | interaction = 'scale',
10 | name,
11 | tooltipClassName = '',
12 | tooltipStyle = {},
13 | ...props
14 | }) => (
15 |
20 |
26 |
27 | )
28 |
29 | export default TaskbarItem
30 |
--------------------------------------------------------------------------------
/src/modules/taskbar/components/atoms/taskbar-item/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent, ButtonHTMLAttributes, CSSProperties } from 'react'
2 |
3 | export interface TaskbarItemProps
4 | extends ButtonHTMLAttributes {
5 | name: string
6 | interaction?: 'scale' | 'fade'
7 | tooltipClassName?: string
8 | tooltipStyle?: CSSProperties
9 | }
10 |
11 | export type TaskbarItemComponent = FunctionComponent
12 |
--------------------------------------------------------------------------------
/src/modules/taskbar/components/atoms/windows-button/index.tsx:
--------------------------------------------------------------------------------
1 | import { useToggleStartMenu, useStartMenuType } from '@stores/start'
2 |
3 | import { TaskbarItem } from '..'
4 |
5 | const size = 24
6 | const gap = 1
7 | const fragment = (size - gap) / 2
8 |
9 | const fragmentSize = {
10 | width: fragment,
11 | height: fragment
12 | }
13 |
14 | const WindowIcon = () => {
15 | const [visible, toggleStartMenu] = useToggleStartMenu()
16 | const [type, updateStartMenuType] = useStartMenuType()
17 |
18 | const toggleOverviewMenu = () => {
19 | if (visible && type !== 'overview') updateStartMenuType('overview')
20 | else toggleStartMenu()
21 | }
22 |
23 |
24 | return (
25 |
26 |
44 |
45 | )
46 | }
47 |
48 | export default WindowIcon
49 |
--------------------------------------------------------------------------------
/src/modules/taskbar/components/index.ts:
--------------------------------------------------------------------------------
1 | export { TaskbarApp, TaskbarItem, WindowButton } from './atoms'
2 | export { DateTime, HiddenItems, Language, Network } from './molecules'
--------------------------------------------------------------------------------
/src/modules/taskbar/components/molecules/datetime/index.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react'
2 |
3 | import dayjs from 'dayjs'
4 |
5 | import { Moon } from 'react-feather'
6 |
7 | import { useSidebar } from '@modules/sidebar/stores'
8 | import { TaskbarItem } from '@modules/taskbar/components'
9 |
10 | const DateTime = () => {
11 | const [, updateSidebar] = useSidebar()
12 |
13 | const [time, updateTime] = useState(dayjs().format('H:mm A'))
14 | const [date, updateDate] = useState(dayjs().format('D/M/YYYY'))
15 |
16 | useEffect(() => {
17 | let time = new Date()
18 |
19 | setTimeout(() => {
20 | setInterval(() => {
21 | updateTime(dayjs().format('H:mm A'))
22 | updateDate(dayjs().format('D/M/YYYY'))
23 | }, 1000)
24 | }, 60000 - time.getSeconds() * 1000 - time.getMilliseconds())
25 | }, [])
26 |
27 | const openSidebar = () => {
28 | updateSidebar(true)
29 | }
30 |
31 | return (
32 |
42 |
43 |
{time}
44 | {date}
45 |
46 |
47 |
48 | )
49 | }
50 |
51 | export default DateTime
52 |
--------------------------------------------------------------------------------
/src/modules/taskbar/components/molecules/hidden/index.tsx:
--------------------------------------------------------------------------------
1 | import { ChevronUp } from 'react-feather'
2 |
3 | import { TaskbarItem } from '../..'
4 |
5 | const HiddenItems = () => (
6 |
7 |
8 |
9 | )
10 |
11 | export default HiddenItems
12 |
--------------------------------------------------------------------------------
/src/modules/taskbar/components/molecules/index.tsx:
--------------------------------------------------------------------------------
1 | export { default as DateTime } from './datetime'
2 | export { default as HiddenItems } from './hidden'
3 | export { default as Language } from './language'
4 | export { default as Network } from './network'
--------------------------------------------------------------------------------
/src/modules/taskbar/components/molecules/language/index.tsx:
--------------------------------------------------------------------------------
1 | import { TaskbarItem } from '../..'
2 |
3 | const Language = () => ENG
4 |
5 | export default Language
6 |
--------------------------------------------------------------------------------
/src/modules/taskbar/components/molecules/network/index.tsx:
--------------------------------------------------------------------------------
1 | import { Volume2, Wifi } from 'react-feather'
2 |
3 | import { TaskbarItem } from '../../atoms'
4 |
5 | const Network = () => (
6 |
11 |
12 |
13 |
14 | )
15 |
16 | export default Network
17 |
--------------------------------------------------------------------------------
/src/modules/taskbar/index.tsx:
--------------------------------------------------------------------------------
1 | import appsMap from '@data/apps'
2 | import { useToggleStartMenu, useStartMenuType } from '@stores/start'
3 |
4 | import {
5 | WindowButton,
6 | DateTime,
7 | Network,
8 | Language,
9 | HiddenItems,
10 | TaskbarApp
11 | } from './components'
12 |
13 | const Taskbar = () => {
14 | const [visible, toggleStartMenu] = useToggleStartMenu()
15 | const [type, updateStartMenuType] = useStartMenuType()
16 |
17 | const toggleSearchMenu = () => {
18 | if (visible && type !== 'search') updateStartMenuType('search')
19 | else {
20 | toggleStartMenu()
21 | updateStartMenuType('search')
22 | }
23 | }
24 |
25 | return (
26 |
50 | )
51 | }
52 |
53 | export default Taskbar
54 |
--------------------------------------------------------------------------------
/src/modules/taskbar/styles.module.sass:
--------------------------------------------------------------------------------
1 | .-interact-scale
2 | & > *
3 | @apply transition-transform
4 |
5 | &:active
6 | & > *
7 | transform: scale(.8)
8 |
9 | .-interact-fade
10 | @apply transition-opacity
11 |
12 | &:active
13 | @apply opacity-50
14 |
15 | .-interact-bg
16 | @apply transition-colors
17 |
18 | &:hover,
19 | &:focus
20 | background-color: rgba(255,255,255,.75)
21 |
--------------------------------------------------------------------------------
/src/services/ease.ts:
--------------------------------------------------------------------------------
1 | export const expo = {
2 | in: [0.16, 1, 0.3, 1],
3 | out: [0.7, 0, 0.84, 0],
4 | inOut: [0.87, 0, 0.13, 1]
5 | }
6 |
--------------------------------------------------------------------------------
/src/stores/apps/index.tsx:
--------------------------------------------------------------------------------
1 | import { atom, useAtom } from 'jotai'
2 |
3 | import { nanoid } from 'nanoid'
4 |
5 | import type { App } from '@data/apps'
6 |
7 | export interface ActiveApp extends App {
8 | id: string
9 | }
10 |
11 | export type ActiveApps = ActiveApp[]
12 |
13 | export interface ActiveAppAppend {
14 | type: 'append'
15 | app: App
16 | }
17 |
18 | export interface ActiveAppClose {
19 | type: 'close'
20 | id: string
21 | }
22 |
23 | export interface ActiveAppPrioritize {
24 | type: 'prioritize'
25 | id: string
26 | }
27 |
28 | export type ActiveAppActions =
29 | | ActiveAppAppend
30 | | ActiveAppClose
31 | | ActiveAppPrioritize
32 |
33 | export const appsBaseAtom = atom([])
34 | export const useBaseApps = () => useAtom(appsBaseAtom)
35 |
36 | export const appAtom = atom(
37 | (get) => get(appsBaseAtom),
38 | (get, set, action) => {
39 | const { type } = action
40 |
41 | switch (type) {
42 | case 'append':
43 | set(appsBaseAtom, [
44 | ...get(appsBaseAtom),
45 | {
46 | ...action.app,
47 | id: nanoid()
48 | }
49 | ])
50 | break
51 |
52 | case 'close':
53 | const apps = [...get(appsBaseAtom)]
54 | const target = apps.findIndex((app) => action.id === app.id)
55 |
56 | if (target < 0) break
57 |
58 | apps.splice(target, 1)
59 |
60 | set(appsBaseAtom, apps)
61 | break
62 |
63 | case 'prioritize':
64 | const activeApps = [...get(appsBaseAtom)]
65 | const prioritizeTarget = activeApps.findIndex(
66 | (app) => action.id === app.id
67 | )
68 |
69 | if (prioritizeTarget < 0) break
70 |
71 | const removed = activeApps.splice(prioritizeTarget, 1)
72 |
73 | set(appsBaseAtom, [...activeApps, ...removed])
74 | break
75 | }
76 | }
77 | )
78 |
79 | export const useApps = () => useAtom(appAtom)
80 |
81 | export const launchAppAtom = atom(null, (_, set, app) => {
82 | set(appAtom, {
83 | type: 'append',
84 | app
85 | })
86 | })
87 |
88 | export const useAppLauncher = (app: App) => {
89 | const launch = useAtom(launchAppAtom)[1]
90 | return () => launch(app)
91 | }
92 |
--------------------------------------------------------------------------------
/src/stores/context-menu/index.ts:
--------------------------------------------------------------------------------
1 | import { atom, useAtom } from 'jotai'
2 |
3 | import { getContextMenuHeight } from '@modules/context-menu/services'
4 |
5 | export type Position = {
6 | top?: number
7 | bottom?: number
8 | left?: number
9 | right?: number
10 | }
11 |
12 | export interface ContextMenuAppend {
13 | type: 'append'
14 | position?: Position
15 | contexts: JSX.Element[][]
16 | index?: number
17 | autoWidth?: boolean
18 | }
19 |
20 | export interface ContextMenuPop {
21 | type: 'pop'
22 | }
23 |
24 | export interface ContextMenuClear {
25 | type: 'clear'
26 | }
27 |
28 | export type ContextMenuActions =
29 | | ContextMenuAppend
30 | | ContextMenuPop
31 | | ContextMenuClear
32 |
33 | export interface ContextMenus {
34 | created: number
35 | position: Position
36 | bottomUp?: boolean
37 | contexts: JSX.Element[][]
38 | }
39 |
40 | export const contextMenuAtom = atom([])
41 | export const contextMenuReducerAtom = atom(
42 | (get) => get(contextMenuAtom),
43 | (get, set, action) => {
44 | const { type } = action
45 |
46 | switch (type) {
47 | case 'append':
48 | const { index = 0 } = action
49 | const menus = get(contextMenuAtom)
50 |
51 | let previousMenus = []
52 | for (let i = 0; i <= index; i++)
53 | if (menus[i]) previousMenus.push(menus[i])
54 |
55 | const { offsetTop, offsetLeft, clientWidth } =
56 | document.getElementById(`context-${index}`) ?? {
57 | offsetTop: 0,
58 | offsetLeft: 0,
59 | clientWidth: 0
60 | }
61 |
62 | let position = {
63 | ...action.position,
64 | top:
65 | action.position?.top ??
66 | (action.position?.bottom ? undefined : offsetTop + 4),
67 | left:
68 | action.position?.left ??
69 | (action.position?.right
70 | ? undefined
71 | : offsetLeft + clientWidth - 4)
72 | }
73 |
74 | const height = getContextMenuHeight(action.contexts)
75 | const bottomUp =
76 | height + (action.position?.top ?? 0) >=
77 | document.body.clientHeight - 56
78 |
79 | if (bottomUp) position.top = position.top! - height
80 |
81 | set(contextMenuAtom, [
82 | ...previousMenus,
83 | {
84 | created: Date.now(),
85 | position,
86 | contexts: action.contexts,
87 | bottomUp
88 | }
89 | ])
90 | break
91 |
92 | case 'pop':
93 | const contexts = [...get(contextMenuAtom)]
94 | contexts.pop()
95 |
96 | set(contextMenuAtom, contexts)
97 | break
98 |
99 | case 'clear':
100 | set(contextMenuAtom, [])
101 | break
102 | }
103 | }
104 | )
105 |
106 | export const useBaseContextMenu = () => useAtom(contextMenuAtom)
107 | export const useContextMenu = () => useAtom(contextMenuReducerAtom)
108 |
--------------------------------------------------------------------------------
/src/stores/index.ts:
--------------------------------------------------------------------------------
1 | export {}
--------------------------------------------------------------------------------
/src/stores/start/index.ts:
--------------------------------------------------------------------------------
1 | import { atom, useAtom } from 'jotai'
2 |
3 | export const startMenuVisibilityAtom = atom(false)
4 | export const useStartMenuVisibility = () => useAtom(startMenuVisibilityAtom)
5 |
6 | export const toggleMenuVisibilityAtom = atom(
7 | (get) => get(startMenuVisibilityAtom),
8 | (get, set) => {
9 | set(startMenuVisibilityAtom, !get(startMenuVisibilityAtom))
10 | }
11 | )
12 | export const useToggleStartMenu = () => useAtom(toggleMenuVisibilityAtom)
13 |
14 | export type StartMenuType = "overview" | "search"
15 |
16 | export const startMenuTypeAtom = atom("overview")
17 | export const useStartMenuType = () => useAtom(startMenuTypeAtom)
18 |
--------------------------------------------------------------------------------
/src/styles/app.sass:
--------------------------------------------------------------------------------
1 | .vibrance
2 | background-color: rgba(255,255,255,.85)
3 | backdrop-filter: blur(24px)
4 | -webkit-backdrop-filter: blur(24px)
5 |
6 | .icon
7 | stroke-width: 1.25
8 | transform: scale(.8)
9 |
10 | &.-mirror
11 | transform: scale(.8) scaleX(-1)
12 |
13 | .light-overlay
14 | @apply transition-colors
15 |
16 | &:hover,
17 | &:focus
18 | background-color: rgba(255,255,255,.5)
--------------------------------------------------------------------------------
/src/styles/fonts.sass:
--------------------------------------------------------------------------------
1 | @font-face
2 | font-family: "Segoe UI Variable Display"
3 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display-Light.ttf") format("truetype")
4 | font-weight: 200
5 | font-display: swap
6 |
7 | @font-face
8 | font-family: "Segoe UI Variable Display"
9 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display-Semilight.ttf") format("truetype")
10 | font-weight: 300
11 | font-display: swap
12 |
13 | @font-face
14 | font-family: "Segoe UI Variable Display"
15 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display.ttf") format("truetype")
16 | font-weight: 400
17 | font-display: swap
18 |
19 | @font-face
20 | font-family: "Segoe UI Variable Display"
21 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display-Semibold.ttf") format("truetype")
22 | font-weight: 600
23 | font-display: swap
24 |
25 | @font-face
26 | font-family: "Segoe UI Variable Display"
27 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Display-Bold.ttf") format("truetype")
28 | font-weight: 700
29 | font-display: swap
30 |
31 | @font-face
32 | font-family: "Segoe UI Variable Small"
33 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small-Light.ttf") format("truetype")
34 | font-weight: 200
35 | font-display: swap
36 |
37 | @font-face
38 | font-family: "Segoe UI Variable Small"
39 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small-Semilight.ttf") format("truetype")
40 | font-weight: 300
41 | font-display: swap
42 |
43 | @font-face
44 | font-family: "Segoe UI Variable Small"
45 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small.ttf") format("truetype")
46 | font-weight: 400
47 | font-display: swap
48 |
49 | @font-face
50 | font-family: "Segoe UI Variable Small"
51 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small-Semibold.ttf") format("truetype")
52 | font-weight: 600
53 | font-display: swap
54 |
55 | @font-face
56 | font-family: "Segoe UI Variable Small"
57 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Small-Bold.ttf") format("truetype")
58 | font-weight: 700
59 | font-display: swap
60 |
61 | @font-face
62 | font-family: "Segoe UI Variable"
63 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text-Light.ttf") format("truetype")
64 | font-weight: 200
65 | font-display: swap
66 |
67 | @font-face
68 | font-family: "Segoe UI Variable"
69 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text-Semilight.ttf") format("truetype")
70 | font-weight: 300
71 | font-display: swap
72 |
73 | @font-face
74 | font-family: "Segoe UI Variable"
75 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text.ttf") format("truetype")
76 | font-weight: 400
77 | font-display: swap
78 |
79 | @font-face
80 | font-family: "Segoe UI Variable"
81 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text-Semibold.ttf") format("truetype")
82 | font-weight: 600
83 | font-display: swap
84 |
85 | @font-face
86 | font-family: "Segoe UI Variable"
87 | src: url("/fonts/segoe-ui-variable/Segoe-UI-Variable-Static-Text-Bold.ttf") format("truetype")
88 | font-weight: 700
89 | font-display: swap
90 |
--------------------------------------------------------------------------------
/src/styles/index.sass:
--------------------------------------------------------------------------------
1 | @use 'fonts'
2 |
3 | html, body, #root
4 | @apply fixed w-full h-screen overflow-hidden
5 | font-family: 'Segoe UI Variable'
6 | overscroll-behavior: contain
7 |
8 | a, button, input
9 | -webkit-tap-highlight-color: rgba(0,0,0,0)
10 |
11 | #wallpaper-blurhash
12 | @apply fixed w-full h-screen transform object-center object-cover scale-x-100
13 | z-index: -1
14 | filter: blur(36px)
--------------------------------------------------------------------------------
/src/styles/tailwind.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | .h-screen {
6 | height: 100vh;
7 | height: -webkit-fill-available;
8 | }
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { FunctionComponent } from "react"
2 |
3 | import type { App, Apps } from "@data/apps"
4 |
5 | export interface AppLauncherProps {
6 | app: App
7 | }
8 |
9 | export type AppLauncherComponent = FunctionComponent
10 |
11 | export interface AppCollectionProps {
12 | apps: Apps
13 | }
14 |
15 | export type AppCollectionComponent = FunctionComponent
16 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const colors = require('tailwindcss/colors')
2 |
3 | module.exports = {
4 | mode: 'jit',
5 | purge: ['./index.html', 'src/**/*.{js,jsx,ts,tsx}'],
6 | darkMode: 'class', // or 'media' or 'class'
7 | theme: {
8 | extend: {
9 | colors: {
10 | sky: colors.sky
11 | }
12 | }
13 | },
14 | variants: {
15 | extend: {}
16 | },
17 | plugins: [require('@tailwindcss/aspect-ratio')]
18 | }
19 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": false,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx",
18 | "baseUrl": "./src",
19 | "paths": {
20 | "@components": ["./components/index.ts"],
21 | "@components/*": ["./components/*"],
22 | "@atoms": ["./components/atoms/index.ts"],
23 | "@atoms/*": ["./components/atoms/*"],
24 | "@molecules": ["./components/molecules/index.ts"],
25 | "@molecules/*": ["./components/molecules/*"],
26 | "@organisms": ["./components/organisms/index.ts"],
27 | "@organisms/*": ["./components/organisms/*"],
28 | "@layouts": ["./layouts/index.ts"],
29 | "@layouts/*": ["./layouts/*"],
30 | "@stores": ["./stores/index.ts"],
31 | "@stores/*": ["./stores/*"],
32 | "@modules": ["./modules/index.ts"],
33 | "@modules/*": ["./modules/*"],
34 | "@services": ["./services/index.ts"],
35 | "@services/*": ["./services/*"],
36 | "@data": ["./data/index.ts"],
37 | "@data/*": ["./data/*"],
38 | "@apps": ["./apps/index.ts"],
39 | "@apps/*": ["./apps/*"],
40 | }
41 | },
42 | "include": ["./src"]
43 | }
44 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react'
2 | // import prefresh from '@prefresh/vite'
3 |
4 | import { defineConfig } from 'vite'
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | plugins: [
9 | react(),
10 | // prefresh()
11 | ],
12 | resolve: {
13 | alias: {
14 | react: 'preact/compat',
15 | 'react-dom': 'preact/compat',
16 | '@components': `${__dirname}/src/components`,
17 | '@atoms': `${__dirname}/src/components/atoms`,
18 | '@molecules': `${__dirname}/src/components/molecules`,
19 | '@organisms': `${__dirname}/src/components/organisms`,
20 | '@layouts': `${__dirname}/src/layouts`,
21 | '@stores': `${__dirname}/src/stores`,
22 | '@modules': `${__dirname}/src/modules`,
23 | '@services': `${__dirname}/src/services`,
24 | '@data': `${__dirname}/src/data`,
25 | '@apps': `${__dirname}/src/apps`
26 | }
27 | }
28 | })
29 |
--------------------------------------------------------------------------------