From 980f15d0a4ed9c593f45556540d9df57914b30ff Mon Sep 17 00:00:00 2001 From: 2977094657 <2977094657@qq.com> Date: Mon, 2 Feb 2026 00:04:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(wrapped-ui):=20=E6=96=B0=E5=A2=9E=20Win98?= =?UTF-8?q?=20=E4=B8=BB=E9=A2=98=E4=B8=8E=E6=A1=8C=E9=9D=A2=E5=8C=96?= =?UTF-8?q?=E5=A4=96=E8=A7=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 主题系统新增 win98(显示名 Windows 98,快捷键扩展到 F1-F4),并区分 retro(pixel/CRT) 与桌面 GUI 主题 - 年度总结页新增 Win98 桌面背景与底部任务栏(背景色/视口高度适配) - 封面与卡片 slide 形态支持 Win98 窗口外观(title bar/icon/controls) - 主题切换器补充 Win98 选项并新增 Win98 专属切换器 - 新增 Win98 图标资源(Start + 桌面图标) --- frontend/assets/css/tailwind.css | 205 ++++++++++++++++++ .../wrapped/shared/WrappedCardShell.vue | 63 +++++- .../wrapped/shared/WrappedControls.vue | 22 ++ .../wrapped/shared/WrappedDeckBackground.vue | 58 ++++- .../components/wrapped/shared/WrappedHero.vue | 75 ++++++- .../wrapped/shared/WrappedThemeSwitcher.vue | 3 +- .../shared/WrappedThemeSwitcherDos.vue | 5 +- .../shared/WrappedThemeSwitcherGameboy.vue | 3 +- .../shared/WrappedThemeSwitcherModern.vue | 3 +- .../shared/WrappedThemeSwitcherWin98.vue | 74 +++++++ .../wrapped/shared/WrappedWin98Taskbar.vue | 169 +++++++++++++++ .../wrapped/shared/WrappedYearSelector.vue | 72 ++++++ frontend/composables/useWrappedTheme.js | 14 +- frontend/pages/wrapped/index.vue | 30 ++- .../assets/images/win98-icons/folder.png | Bin 0 -> 571 bytes .../public/assets/images/win98-icons/mail.png | Bin 0 -> 696 bytes .../assets/images/win98-icons/photos.png | Bin 0 -> 773 bytes .../assets/images/win98-icons/recycle.png | Bin 0 -> 388 bytes frontend/public/assets/images/windows-0.png | Bin 0 -> 458 bytes 19 files changed, 767 insertions(+), 29 deletions(-) create mode 100644 frontend/components/wrapped/shared/WrappedThemeSwitcherWin98.vue create mode 100644 frontend/components/wrapped/shared/WrappedWin98Taskbar.vue create mode 100644 frontend/public/assets/images/win98-icons/folder.png create mode 100644 frontend/public/assets/images/win98-icons/mail.png create mode 100644 frontend/public/assets/images/win98-icons/photos.png create mode 100644 frontend/public/assets/images/win98-icons/recycle.png create mode 100644 frontend/public/assets/images/windows-0.png diff --git a/frontend/assets/css/tailwind.css b/frontend/assets/css/tailwind.css index 84a4645..96d1bc2 100644 --- a/frontend/assets/css/tailwind.css +++ b/frontend/assets/css/tailwind.css @@ -1287,3 +1287,208 @@ color: #000000 !important; text-shadow: none; } + +/* ============================================ + Theme 3: Windows 95/98 - Win98 + ============================================ */ +.wrapped-theme-win98 { + /* System-like colors (approx.) */ + --win98-face: #c0c0c0; /* ButtonFace */ + --win98-hi: #ffffff; /* ButtonHighlight */ + --win98-shadow: #808080; /* ButtonShadow */ + --win98-dkshadow: #000000; /* Black */ + --win98-outline: #dedede; /* Extra light line (common in Win95 clones) */ + --win98-dither: repeating-conic-gradient(#bdbebd 0% 25%, #ffffff 0% 50%) 50% / 2px 2px; + + --win98-title: #000080; /* ActiveCaption */ + --win98-title2: #1084d0; /* Caption gradient (approx.) */ + --win98-title-text: #ffffff; + --win98-title-inactive: #7b7d7b; + --win98-title-inactive-text: #e6e6e6; + + /* Map to Wrapped theme variables */ + --wrapped-bg: #ffffff; /* fields/content */ + --wrapped-card-bg: var(--win98-face); + --wrapped-text: #000000; + --wrapped-text-secondary: #404040; + --wrapped-accent: var(--win98-title); + --wrapped-border: var(--win98-shadow); + --wrapped-warning: #800000; + + font-family: "MS Sans Serif", Tahoma, "Microsoft Sans Serif", "Segoe UI", sans-serif !important; +} + +/* Win98: hard edges */ +.wrapped-theme-win98 [class*="rounded"] { + border-radius: 0 !important; +} + +/* Win98: baseline typography (override Tailwind inline colors via !important) */ +.wrapped-theme-win98 .wrapped-title { + color: var(--wrapped-text) !important; +} + +.wrapped-theme-win98 .wrapped-body, +.wrapped-theme-win98 .wrapped-label { + color: var(--wrapped-text-secondary) !important; +} + +.wrapped-theme-win98 .wrapped-number { + color: var(--wrapped-accent) !important; +} + +/* Win98: generic raised button */ +.wrapped-theme-win98 button { + background: var(--win98-face) !important; + color: #000000 !important; + border-radius: 0 !important; + text-shadow: none !important; + + /* Win95-ish bevel: light (top/left) + shadow (bottom/right) + hard drop */ + border-top: 1px solid var(--win98-hi) !important; + border-left: 1px solid var(--win98-hi) !important; + border-right: 1px solid var(--win98-shadow) !important; + border-bottom: 1px solid var(--win98-shadow) !important; + box-shadow: 1px 1px 0 var(--win98-dkshadow) !important; +} + +.wrapped-theme-win98 button:active:not(:disabled) { + background: var(--win98-face) !important; + box-shadow: none !important; + border-top: 1px solid var(--win98-dkshadow) !important; + border-left: 1px solid var(--win98-dkshadow) !important; + border-right: 1px solid var(--win98-hi) !important; + border-bottom: 1px solid var(--win98-hi) !important; +} + +.wrapped-theme-win98 button:disabled { + opacity: 0.65; + cursor: not-allowed; +} + +/* Win98: dotted focus rectangle (classic) */ +.wrapped-theme-win98 button:focus-visible { + outline: 1px dotted var(--win98-dkshadow); + outline-offset: -4px; +} + +/* Win98 helper: checkered/dither fill for "pressed/toggled" UI (taskbar active, start menu pressed, etc.) */ +.wrapped-theme-win98 .win98-dither { + background: var(--win98-dither) !important; +} + +/* Win98: text-ish inputs / selects get a sunken look (avoid checkbox/radio etc.) */ +.wrapped-theme-win98 textarea, +.wrapped-theme-win98 select, +.wrapped-theme-win98 input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]) { + background: var(--wrapped-bg) !important; + color: var(--wrapped-text) !important; + border-radius: 0 !important; + border: 1px solid var(--win98-shadow) !important; + box-shadow: + inset 1px 1px 0 var(--win98-dkshadow), + inset -1px -1px 0 var(--win98-hi); +} + +/* Win98: window primitives (98.css-like semantics) */ +.wrapped-theme-win98 .window { + background: var(--win98-face); + + /* Stronger Win95-ish window frame (inspired by /win95/assets/css/windows/window.css) */ + border-top: 2px solid var(--win98-hi); + border-left: 2px solid var(--win98-hi); + border-right: 1.5px solid var(--win98-shadow); + border-bottom: 1.5px solid var(--win98-shadow); + box-shadow: 1.5px 1.5px 0 var(--win98-dkshadow); + outline: 1px solid var(--win98-outline); +} + +.wrapped-theme-win98 .title-bar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 3px 4px; + + /* Win95 is typically solid; Win98 sometimes gradients. Keep solid by default. */ + background: var(--win98-title); + color: var(--win98-title-text); + font-weight: 700; + user-select: none; +} + +.wrapped-theme-win98 .title-bar-text { + display: flex; + align-items: center; + gap: 6px; + min-width: 0; + font-size: 11px; + line-height: 1; +} + +.wrapped-theme-win98 .title-bar-icon { + width: 16px; + height: 16px; + flex: none; + image-rendering: pixelated; +} + +.wrapped-theme-win98 .title-bar-text span { + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.wrapped-theme-win98 .title-bar-controls { + display: inline-flex; + gap: 2px; +} + +.wrapped-theme-win98 .title-bar-controls button { + width: 18px; + height: 16px; + padding: 0; + display: inline-flex; + align-items: center; + justify-content: center; + position: relative; + font-weight: 700; + line-height: 1; +} + +.wrapped-theme-win98 .title-bar-controls button::before { + content: ""; + display: block; +} + +/* Win95-ish glyphs (avoid relying on fonts for the line/square). */ +.wrapped-theme-win98 .title-bar-controls button[aria-label="Minimize"]::before { + width: 6px; + height: 2px; + background: #000000; + transform: translateY(4px); +} + +.wrapped-theme-win98 .title-bar-controls button[aria-label="Maximize"]::before { + width: 9px; + height: 8px; + box-sizing: border-box; + border: 1px solid #000000; + border-top-width: 2px; + background: transparent; +} + +.wrapped-theme-win98 .title-bar-controls button[aria-label="Close"]::before { + content: "×"; + font-size: 14px; + line-height: 1; + transform: translateY(-1px); +} + +.wrapped-theme-win98 .window-body { + padding: 8px; + background: var(--win98-face); + border-top: 1px solid var(--win98-shadow); + box-shadow: inset 0 1px 0 var(--win98-hi); +} diff --git a/frontend/components/wrapped/shared/WrappedCardShell.vue b/frontend/components/wrapped/shared/WrappedCardShell.vue index d86256a..837edaf 100644 --- a/frontend/components/wrapped/shared/WrappedCardShell.vue +++ b/frontend/components/wrapped/shared/WrappedCardShell.vue @@ -1,5 +1,5 @@ @@ -49,6 +81,15 @@ defineProps({ narrative: { type: String, default: '' }, variant: { type: String, default: 'panel' } // 'panel' | 'slide' }) + +const { theme } = useWrappedTheme() +const isWin98 = computed(() => theme.value === 'win98') + +const slideContainerClass = computed(() => ( + isWin98.value + ? 'relative h-full max-w-5xl mx-auto px-6 pt-2 pb-4 sm:px-8 sm:pt-3 sm:pb-6 flex flex-col' + : 'relative h-full max-w-5xl mx-auto px-6 py-10 sm:px-8 sm:py-12 flex flex-col' +)) diff --git a/frontend/components/wrapped/shared/WrappedDeckBackground.vue b/frontend/components/wrapped/shared/WrappedDeckBackground.vue index d30c074..81bc08d 100644 --- a/frontend/components/wrapped/shared/WrappedDeckBackground.vue +++ b/frontend/components/wrapped/shared/WrappedDeckBackground.vue @@ -1,6 +1,6 @@ + + + diff --git a/frontend/components/wrapped/shared/WrappedHero.vue b/frontend/components/wrapped/shared/WrappedHero.vue index f2b4d39..93669ad 100644 --- a/frontend/components/wrapped/shared/WrappedHero.vue +++ b/frontend/components/wrapped/shared/WrappedHero.vue @@ -9,7 +9,57 @@
@@ -26,7 +26,8 @@ const { theme, setTheme } = useWrappedTheme() const themes = [ { value: 'off', label: 'Modern' }, { value: 'gameboy', label: 'GameBoy' }, - { value: 'dos', label: 'DOS' } + { value: 'dos', label: 'DOS' }, + { value: 'win98', label: 'Win98' } ] diff --git a/frontend/components/wrapped/shared/WrappedThemeSwitcherGameboy.vue b/frontend/components/wrapped/shared/WrappedThemeSwitcherGameboy.vue index 904f6c8..de24aa6 100644 --- a/frontend/components/wrapped/shared/WrappedThemeSwitcherGameboy.vue +++ b/frontend/components/wrapped/shared/WrappedThemeSwitcherGameboy.vue @@ -25,7 +25,8 @@ const { theme, setTheme } = useWrappedTheme() const themes = [ { value: 'off', label: 'MODERN' }, { value: 'gameboy', label: 'GAME BOY' }, - { value: 'dos', label: 'DOS' } + { value: 'dos', label: 'DOS' }, + { value: 'win98', label: 'WIN98' } ] const selectTheme = (value) => { diff --git a/frontend/components/wrapped/shared/WrappedThemeSwitcherModern.vue b/frontend/components/wrapped/shared/WrappedThemeSwitcherModern.vue index c65b74d..cd2ebcc 100644 --- a/frontend/components/wrapped/shared/WrappedThemeSwitcherModern.vue +++ b/frontend/components/wrapped/shared/WrappedThemeSwitcherModern.vue @@ -25,6 +25,7 @@ const { theme, setTheme } = useWrappedTheme() const themes = [ { value: 'off', label: 'Modern' }, { value: 'gameboy', label: 'Game Boy' }, - { value: 'dos', label: 'DOS' } + { value: 'dos', label: 'DOS' }, + { value: 'win98', label: 'Win98' } ] diff --git a/frontend/components/wrapped/shared/WrappedThemeSwitcherWin98.vue b/frontend/components/wrapped/shared/WrappedThemeSwitcherWin98.vue new file mode 100644 index 0000000..b3e2e88 --- /dev/null +++ b/frontend/components/wrapped/shared/WrappedThemeSwitcherWin98.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/frontend/components/wrapped/shared/WrappedWin98Taskbar.vue b/frontend/components/wrapped/shared/WrappedWin98Taskbar.vue new file mode 100644 index 0000000..cb8c659 --- /dev/null +++ b/frontend/components/wrapped/shared/WrappedWin98Taskbar.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/frontend/components/wrapped/shared/WrappedYearSelector.vue b/frontend/components/wrapped/shared/WrappedYearSelector.vue index 00e3873..d5425bd 100644 --- a/frontend/components/wrapped/shared/WrappedYearSelector.vue +++ b/frontend/components/wrapped/shared/WrappedYearSelector.vue @@ -38,6 +38,25 @@ >[+]
+ +
+
+ + {{ modelValue }}年 + +
+
+
@@ -240,4 +259,57 @@ onBeforeUnmount(() => { min-width: 50px; text-align: center; } + +/* ========== Win98 风格 ========== */ +.year-win98 { + font-family: "MS Sans Serif", Tahoma, "Microsoft Sans Serif", "Segoe UI", sans-serif; + font-size: 11px; +} + +.win98-year-box { + display: inline-flex; + align-items: center; + background: #c0c0c0; + padding: 2px; + border: 1px solid #808080; + box-shadow: + inset 1px 1px 0 #ffffff, + inset -1px -1px 0 #000000; +} + +.win98-year-value { + min-width: 62px; + text-align: center; + color: #000000; + padding: 2px 8px; + background: #ffffff; + border: 1px solid #808080; + box-shadow: + inset 1px 1px 0 #000000, + inset -1px -1px 0 #ffffff; +} + +.win98-arrow { + width: 24px; + height: 22px; + background: #c0c0c0; + border: 1px solid #808080; + box-shadow: + inset 1px 1px 0 #ffffff, + inset -1px -1px 0 #000000; + cursor: pointer; + font: inherit; + line-height: 1; +} + +.win98-arrow:active:not(:disabled) { + box-shadow: + inset 1px 1px 0 #000000, + inset -1px -1px 0 #ffffff; +} + +.win98-arrow:disabled { + opacity: 0.5; + cursor: not-allowed; +} diff --git a/frontend/composables/useWrappedTheme.js b/frontend/composables/useWrappedTheme.js index ed54aeb..e56be6b 100644 --- a/frontend/composables/useWrappedTheme.js +++ b/frontend/composables/useWrappedTheme.js @@ -4,7 +4,8 @@ */ const STORAGE_KEY = 'wrapped-theme' -const VALID_THEMES = ['off', 'gameboy', 'dos'] +const VALID_THEMES = ['off', 'gameboy', 'dos', 'win98'] +const RETRO_THEMES = new Set(['gameboy', 'dos']) // 全局响应式状态(跨组件共享) const theme = ref('off') @@ -54,7 +55,10 @@ export function useWrappedTheme() { // 计算属性:当前主题的 CSS 类名 const themeClass = computed(() => { if (theme.value === 'off') return '' - return `wrapped-retro wrapped-theme-${theme.value}` + // Note: not every non-modern theme is "retro pixel/CRT". + // Keep wrapped-retro for themes that rely on pixel/CRT shared styles. + const base = RETRO_THEMES.has(theme.value) ? 'wrapped-retro ' : '' + return `${base}wrapped-theme-${theme.value}` }) // 计算属性:主题显示名称 @@ -62,7 +66,8 @@ export function useWrappedTheme() { const names = { off: 'Modern', gameboy: 'Game Boy', - dos: 'DOS Terminal' + dos: 'DOS Terminal', + win98: 'Windows 98' } return names[theme.value] || 'Modern' }) @@ -88,6 +93,9 @@ export function useWrappedTheme() { } else if (e.key === 'F3') { e.preventDefault() setTheme('dos') + } else if (e.key === 'F4') { + e.preventDefault() + setTheme('win98') } } diff --git a/frontend/pages/wrapped/index.vue b/frontend/pages/wrapped/index.vue index 25f6880..1f2b2bc 100644 --- a/frontend/pages/wrapped/index.vue +++ b/frontend/pages/wrapped/index.vue @@ -7,7 +7,8 @@ > - + +
@@ -164,6 +165,12 @@
+ + +
@@ -219,7 +226,8 @@ let navUnlockTimer = null const THEME_BG = { off: '#F3FFF8', // Modern: 浅绿 gameboy: '#9bbc0f', // Game Boy: 亮绿 - dos: '#0a0a0a' // DOS: 黑色 + dos: '#0a0a0a', // DOS: 黑色 + win98: '#008080' // Win98: 经典桌面青色 } const slides = computed(() => { @@ -229,6 +237,14 @@ const slides = computed(() => { return out }) +const taskbarTitle = computed(() => { + if (theme.value !== 'win98') return '' + if (activeIndex.value === 0) return `${year.value} WeChat Wrapped` + const idx = activeIndex.value - 1 + const c = report.value?.cards?.[idx] + return String(c?.title || 'WeChat Wrapped') +}) + const currentBg = computed(() => THEME_BG[theme.value] || THEME_BG.off) const slideStyle = computed(() => ( @@ -356,8 +372,11 @@ const onTouchEnd = (e) => { const updateViewport = () => { const h = deckEl.value?.clientHeight || window.innerHeight || 0 if (!h) return + // Reserve space for the Win98 taskbar at the bottom. + const offset = theme.value === 'win98' ? 40 : 0 + const effective = Math.max(0, h - offset) // Avoid endless reflows from 1px rounding errors (especially in Electron). - if (Math.abs(viewportHeight.value - h) > 1) viewportHeight.value = h + if (Math.abs(viewportHeight.value - effective) > 1) viewportHeight.value = effective } const loadAccounts = async () => { @@ -515,6 +534,11 @@ onMounted(async () => { } }) +// Theme switch may change reserved UI space (e.g., Win98 taskbar) +watch(theme, () => { + updateViewport() +}) + onBeforeUnmount(() => { window.removeEventListener('resize', updateViewport) deckEl.value?.removeEventListener('wheel', onWheel) diff --git a/frontend/public/assets/images/win98-icons/folder.png b/frontend/public/assets/images/win98-icons/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..136be6a49a85308a61968741d8fadc7be9334cb0 GIT binary patch literal 571 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDB3?!H8JlO)ISkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq8r{zDi(Vu$sZZAYL$MSD+10LL$H?#1%--oCzcte*F0H z|NqQ0XVU(knfd?!|DW@EHvvT%OM?7@862M7NCR<_yxm>c#S%MS13By^p1!W^kJu!{ z8HG73<0k{9g*{yyLo808oqU$}kO5EYX^z~Zdy|+$#2hx&JU_rT?YlhVZ_7+!;lgJt zvs&Zr{=52I=0AJklj#087iv!*^IF&wBX_hSFIhpEeZoXxj}u>OUzvvAoLDNK^*TtD z(^&uJ2L(<~GaZ*BOF9KME^wHcP<`XGD)aNRuQ>I)`*}F}Tx1X4n{57IURevngDaxp z!e!CS`z<%9g|a%meIldF=slTR%cWB})I_uAqs!%WXM*+DEjcUGm+){!Z1;2*IgW$w9AeBGio=7pC9>E_ zt+bK(?Uq>Pp&h7aGRH_EZGnXEM30N_O69U<<|WtuZT%l3zrO47* Tn@_5M8W=oX{an^LB{Ts5_sh{q literal 0 HcmV?d00001 diff --git a/frontend/public/assets/images/win98-icons/mail.png b/frontend/public/assets/images/win98-icons/mail.png new file mode 100644 index 0000000000000000000000000000000000000000..d5cb3ec0761098e5f055cf5f4fa6346397e24308 GIT binary patch literal 696 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDB3?!H8JlO)ISkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq8r{zDi(Vu$sZZAYL$MSD+10LNUN6#1*8Vq2a)R13>Ek z|NjgPK!N`t0!V>~hM*L)P$11%666=m;PC858i^mS!_#3msw zXz=ik%?6-!yQhm|h{fsL%Nw%}8wju^{N`Pi(DXv>ChK}$lj;_!UE3Z6?YVaC!N!u4 z&!+Iq^j7eHknm@Ut*3hQs@e%p|9^VkqU=6Vc9Fe7d7#0^yIn3V%QJs@{F!q^=V43! zk=YMg^|h6@*ra4KKIUw3o7bJoYxLGr#cN^7)6%B(fhCJG+;{xzTl74+4S}TF3=0|jFK9%wBuWK6WB9s7#dFDy9|6<8#jJLD^km24*XlQ{ zf823eTw?#Uc~$p0oz6-I^Q9jm3bPXW1I)axZriwV!||uhLB1CX3W9wv>mJ-4bU2~m zj5J#Z4+EF^Wd)<0HB)mPoH(ycMDg>( znp=Wdd`}P9tdZI0|F5}j-_CQ4RmJ%`FC1ueS@v0gh2t5+T6TpQ!nbaRH>A&8V{}gS zS<5y)7d1wS!i2m-GqWEiv1xE8wE8<7|L%60CHP&-*&-3M^J_P>-r@|Y5ikG% literal 0 HcmV?d00001 diff --git a/frontend/public/assets/images/win98-icons/photos.png b/frontend/public/assets/images/win98-icons/photos.png new file mode 100644 index 0000000000000000000000000000000000000000..8ba4cf6f63dbd30f067c4c195df74232d0f5a4f1 GIT binary patch literal 773 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDB3?!H8JlO)ISkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq8r{zDi(Vu$sZZAYL$MSD+10LOsAI#1*8Vq2a)R1ONa3 z2eLsNFb1)JT#z6LFVTII3FI=C1o;IsI6S+N2I3@nySp$8^7woLa@b2eeO=ifu?RCN z36<^@J`R*V=jq}YVsZNF#8vRo-3JU-zVLn)7_Mv%2(r;SVAl zE5pk2T0&hD)< z)HWV*?z73@wu*eX-#1E9Icnw~tCT`H$LNMvzBATH&pxIwC$NdX|Im#)%!_vIoVnXr zh|%q9k*Zpm%%nw4Qw^&mcwY3#AANE+K{-%p*N$Dgu54vI_aa={<-yLkjL!@tj!2ei zYJUlH-8khm=gzM604j_)h| z*is{VvX~!Dn8F;o)uz);VAqw#HCo-1Z!DIZ%Kq0@xvA9d_4KF3cSIwY5AcaUtacBP zH{F@d)93MK`DvAh`=7KQUdC?w_p=JiL9-wM_nOU~uy9Zl>%P1x54pFO?KpesXiV zIK$#jp{Yfk{q!r%+t(#I1^6xxzH&SM`PC=-JHj{G-r?E{N`|T>t`Q|Ei6yC4$wjF^ ziowXh&_dV1MAy(V#K6$X*u)CVwK6c6oj2t#iiX_$l+3hB+!|gP>j2X;1B0ilpUXO@ GgeCyI+cmcU literal 0 HcmV?d00001 diff --git a/frontend/public/assets/images/win98-icons/recycle.png b/frontend/public/assets/images/win98-icons/recycle.png new file mode 100644 index 0000000000000000000000000000000000000000..74d32be62a41fec225afa774ed354bdfa7070100 GIT binary patch literal 388 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-G$+Qd;gjJKpuOE zr>`sfBQ^`K~!Cw@Y2n zm`_olS-VfM!}w=!uluWM)eRFB%}(y`WIWK%5TY=pK`62G=|aYZ;#$m1%?EEx-hJpm zxj<#ke@|;J=1GikCMym0JH1!Hwa+%!y6+9QH;?u^kss zco%iCN?+UG_p)NV!8ZNXKzFH@xJHzuB$lLFB^RXvDF!10LknF4GhHK-5F=wN19K}w pBV7X%D+7Z^b1SM*H00)|WTsW()-d_Nf#X0844$rjF6*2UngH+yen9{L literal 0 HcmV?d00001 diff --git a/frontend/public/assets/images/windows-0.png b/frontend/public/assets/images/windows-0.png new file mode 100644 index 0000000000000000000000000000000000000000..572511dd5cf5876d80417f6418a3a76a23f4c36a GIT binary patch literal 458 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvmUKs7M+SzC{oH>NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fgp%~y3;tHgJpn-wmKac`q zAPda<|DU0uf#LuE|B|QOF95|DOM?7@862M7NCR<_yxm>6GA?`x19I3)Jbhi+AF)X> zSjea-Z8`=Nn(67{7@~20>qSGpLk2vo0nu`MZt=`HtQ7u&b%I65@@HJJH!bwalvX&( z9=u)e^=Q4=gZ0g;wBjo{&godGI~sM(o23_ic5~B=3ry1(7ici$FW4dQK7z6PMj3C$~f0J#PYnj+9+s}C6=HJEbpA-9S z)^0uiqhP7K>_Pee2h>lpD!ebBrq~N~q-u$4L`h0wNvc(HQ7VvPFfuT-&^0jAH8Kk^ vFt9Q;voZp5O{@$IdOv5aLeY?$pOTqYiCe>;b(h3|8W=oX{an^LB{Ts5LXnr; literal 0 HcmV?d00001