|
| 1 | +import { wrapText } from '../utils'; |
| 2 | + |
| 3 | +export const win95 = (ctx, width, height, scale, data) => { |
| 4 | + const { repoOwner, repoName, description } = data; |
| 5 | + // WINDOWS 95 Style |
| 6 | + |
| 7 | + // 1. Desktop Background (Teal) |
| 8 | + ctx.fillStyle = '#008080'; |
| 9 | + ctx.fillRect(0, 0, width, height); |
| 10 | + |
| 11 | + // 2. Main Window |
| 12 | + const winW = width * 0.7; |
| 13 | + const winH = height * 0.6; |
| 14 | + const winX = (width - winW) / 2; |
| 15 | + const winY = (height - winH) / 2; |
| 16 | + |
| 17 | + const gray = '#c0c0c0'; |
| 18 | + const darkGray = '#808080'; |
| 19 | + const white = '#ffffff'; |
| 20 | + const black = '#000000'; |
| 21 | + const navy = '#000080'; |
| 22 | + |
| 23 | + // Window Body |
| 24 | + ctx.fillStyle = gray; |
| 25 | + ctx.fillRect(winX, winY, winW, winH); |
| 26 | + |
| 27 | + // 3D Borders (Bevel) |
| 28 | + const drawBevel = (x, y, w, h, isPressed = false) => { |
| 29 | + const t = 2 * scale; // thickness |
| 30 | + // Top/Left |
| 31 | + ctx.fillStyle = isPressed ? darkGray : white; |
| 32 | + ctx.fillRect(x, y, w, t); // Top |
| 33 | + ctx.fillRect(x, y, t, h); // Left |
| 34 | + |
| 35 | + // Bottom/Right |
| 36 | + ctx.fillStyle = isPressed ? white : black; |
| 37 | + ctx.fillRect(x, y + h - t, w, t); // Bottom |
| 38 | + ctx.fillRect(x + w - t, y, t, h); // Right |
| 39 | + |
| 40 | + // Inner shadow for unpressed |
| 41 | + if (!isPressed) { |
| 42 | + ctx.fillStyle = darkGray; |
| 43 | + ctx.fillRect(x + t, y + h - t*2, w - t*2, t); |
| 44 | + ctx.fillRect(x + w - t*2, y + t, t, h - t*2); |
| 45 | + } |
| 46 | + }; |
| 47 | + |
| 48 | + drawBevel(winX, winY, winW, winH); |
| 49 | + |
| 50 | + // Title Bar |
| 51 | + const titleH = 40 * scale; |
| 52 | + const titlePad = 4 * scale; |
| 53 | + ctx.fillStyle = navy; |
| 54 | + ctx.fillRect(winX + titlePad, winY + titlePad, winW - titlePad*2, titleH); |
| 55 | + |
| 56 | + // Title Text |
| 57 | + ctx.fillStyle = white; |
| 58 | + ctx.textAlign = 'left'; |
| 59 | + ctx.font = `bold ${20 * scale}px "Arial", sans-serif`; |
| 60 | + ctx.fillText(`${repoOwner} - Notepad`, winX + titlePad + 10*scale, winY + titlePad + 28*scale); |
| 61 | + |
| 62 | + // X Button |
| 63 | + const btnSize = titleH - 4*scale; |
| 64 | + const btnX = winX + winW - titlePad - btnSize - 2*scale; |
| 65 | + const btnY = winY + titlePad + 2*scale; |
| 66 | + ctx.fillStyle = gray; |
| 67 | + ctx.fillRect(btnX, btnY, btnSize, btnSize); |
| 68 | + drawBevel(btnX, btnY, btnSize, btnSize); |
| 69 | + ctx.fillStyle = black; |
| 70 | + ctx.textAlign = 'center'; |
| 71 | + ctx.font = `bold ${18 * scale}px "Arial", sans-serif`; |
| 72 | + ctx.fillText("X", btnX + btnSize/2, btnY + btnSize/2 + 6*scale); |
| 73 | + |
| 74 | + // Menu Bar (File Edit View...) |
| 75 | + const menuY = winY + titlePad + titleH; |
| 76 | + |
| 77 | + ctx.fillStyle = black; |
| 78 | + ctx.textAlign = 'left'; |
| 79 | + ctx.font = `normal ${18 * scale}px "Arial", sans-serif`; |
| 80 | + ctx.fillText("File Edit Search Help", winX + 15*scale, menuY + 20*scale); |
| 81 | + |
| 82 | + // Text Area (White input box) |
| 83 | + const areaX = winX + 10*scale; |
| 84 | + const areaY = menuY + 30*scale; |
| 85 | + const areaW = winW - 20*scale; |
| 86 | + const areaH = winH - (areaY - winY) - 10*scale; |
| 87 | + |
| 88 | + ctx.fillStyle = white; |
| 89 | + ctx.fillRect(areaX, areaY, areaW, areaH); |
| 90 | + // Inset border for text area |
| 91 | + ctx.fillStyle = darkGray; // Top/Left shadow |
| 92 | + ctx.fillRect(areaX, areaY, areaW, 2*scale); |
| 93 | + ctx.fillRect(areaX, areaY, 2*scale, areaH); |
| 94 | + ctx.fillStyle = '#dfdfdf'; // Bottom/Right highlight (light gray) |
| 95 | + ctx.fillRect(areaX, areaY + areaH - 2*scale, areaW, 2*scale); |
| 96 | + ctx.fillRect(areaX + areaW - 2*scale, areaY, 2*scale, areaH); |
| 97 | + |
| 98 | + // Content Text |
| 99 | + ctx.fillStyle = black; |
| 100 | + ctx.font = `bold ${60 * scale}px "Courier New", monospace`; // Monospace for that raw feel |
| 101 | + ctx.fillText(repoName, areaX + 20*scale, areaY + 80*scale); |
| 102 | + |
| 103 | + ctx.font = `normal ${24 * scale}px "Courier New", monospace`; |
| 104 | + wrapText(ctx, description, areaX + 20*scale, areaY + 140*scale, areaW - 40*scale, 35*scale); |
| 105 | + |
| 106 | + // Cursor (Blinking) |
| 107 | + // We can't actually blink in static canvas, but we draw it |
| 108 | + // Let's put it after the description? Or just at the end. |
| 109 | + // Simplifying: Just drawing it below description |
| 110 | + // ctx.fillRect(areaX + 20*scale, areaY + 200*scale, 15*scale, 2*scale); // Underscore cursor |
| 111 | + |
| 112 | + // 3. Desktop Icons (Left side) |
| 113 | + const iconSize = 60 * scale; |
| 114 | + const startIconY = 40 * scale; |
| 115 | + const iconGap = 100 * scale; |
| 116 | + |
| 117 | + const drawIcon = (label, y, type='folder') => { |
| 118 | + const x = 40 * scale; |
| 119 | + // Icon graphic (Simple pixel art approx) |
| 120 | + if (type === 'pc') { |
| 121 | + ctx.fillStyle = black; |
| 122 | + ctx.fillRect(x, y, iconSize, iconSize * 0.8); |
| 123 | + ctx.fillStyle = '#00ffff'; // Screen |
| 124 | + ctx.fillRect(x+4*scale, y+4*scale, iconSize-8*scale, iconSize*0.5); |
| 125 | + } else { |
| 126 | + // Folder |
| 127 | + ctx.fillStyle = '#ffd700'; |
| 128 | + ctx.beginPath(); |
| 129 | + ctx.moveTo(x, y); |
| 130 | + ctx.lineTo(x + iconSize*0.4, y); |
| 131 | + ctx.lineTo(x + iconSize*0.5, y + iconSize*0.1); |
| 132 | + ctx.lineTo(x + iconSize, y + iconSize*0.1); |
| 133 | + ctx.lineTo(x + iconSize, y + iconSize*0.8); |
| 134 | + ctx.lineTo(x, y + iconSize*0.8); |
| 135 | + ctx.fill(); |
| 136 | + ctx.stroke(); |
| 137 | + } |
| 138 | + |
| 139 | + // Label |
| 140 | + ctx.fillStyle = white; // Icon text usually has transparent bg in 95 but let's just do white text with shadow |
| 141 | + ctx.font = `normal ${16 * scale}px "Arial", sans-serif`; |
| 142 | + ctx.textAlign = 'center'; |
| 143 | + |
| 144 | + // Dotted focus rect for selected icon? |
| 145 | + // ctx.setLineDash([2, 2]); |
| 146 | + // ctx.strokeStyle = white; |
| 147 | + // ctx.strokeRect(x - 10*scale, y + iconSize, iconSize + 20*scale, 20*scale); |
| 148 | + // ctx.setLineDash([]); |
| 149 | + |
| 150 | + ctx.fillText(label, x + iconSize/2, y + iconSize + 20*scale); |
| 151 | + }; |
| 152 | + |
| 153 | + drawIcon("My Computer", startIconY, 'pc'); |
| 154 | + drawIcon("Network", startIconY + iconGap, 'pc'); |
| 155 | + drawIcon("Recycle Bin", startIconY + iconGap*2, 'folder'); |
| 156 | + |
| 157 | + // 4. Taskbar |
| 158 | + const taskH = 40 * scale; |
| 159 | + const taskY = height - taskH; |
| 160 | + ctx.fillStyle = gray; |
| 161 | + ctx.fillRect(0, taskY, width, taskH); |
| 162 | + ctx.fillStyle = white; // Top highlight |
| 163 | + ctx.fillRect(0, taskY, width, 2*scale); |
| 164 | + |
| 165 | + // Start Button |
| 166 | + const startW = 100 * scale; |
| 167 | + const startH = taskH - 6*scale; |
| 168 | + const startX = 4*scale; |
| 169 | + const startY = taskY + 3*scale; |
| 170 | + drawBevel(startX, startY, startW, startH); |
| 171 | + |
| 172 | + ctx.fillStyle = black; |
| 173 | + ctx.font = `bold ${18 * scale}px "Arial", sans-serif`; |
| 174 | + ctx.textAlign = 'left'; |
| 175 | + ctx.fillText("Start", startX + 35*scale, startY + 22*scale); |
| 176 | + |
| 177 | + // Windows Logo on start button (Simple blocks) |
| 178 | + const logoX = startX + 6*scale; |
| 179 | + const logoY = startY + 6*scale; |
| 180 | + const logoS = 20*scale; |
| 181 | + ctx.fillStyle = '#ff0000'; ctx.fillRect(logoX, logoY, logoS/2, logoS/2); |
| 182 | + ctx.fillStyle = '#00ff00'; ctx.fillRect(logoX + logoS/2, logoY, logoS/2, logoS/2); |
| 183 | + ctx.fillStyle = '#0000ff'; ctx.fillRect(logoX, logoY + logoS/2, logoS/2, logoS/2); |
| 184 | + ctx.fillStyle = '#ffff00'; ctx.fillRect(logoX + logoS/2, logoY + logoS/2, logoS/2, logoS/2); |
| 185 | + |
| 186 | + // Tray Area (Time) |
| 187 | + const trayW = 100 * scale; |
| 188 | + const trayX = width - trayW - 4*scale; |
| 189 | + const trayY = taskY + 4*scale; |
| 190 | + const trayH = taskH - 8*scale; |
| 191 | + |
| 192 | + // Sunken tray |
| 193 | + ctx.fillStyle = white; ctx.fillRect(trayX + trayW - 2*scale, trayY, 2*scale, trayH); // Right |
| 194 | + ctx.fillRect(trayX, trayY + trayH - 2*scale, trayW, 2*scale); // Bottom |
| 195 | + ctx.fillStyle = darkGray; ctx.fillRect(trayX, trayY, trayW, 2*scale); // Top |
| 196 | + ctx.fillRect(trayX, trayY, 2*scale, trayH); // Left |
| 197 | + |
| 198 | + ctx.fillStyle = black; |
| 199 | + ctx.textAlign = 'center'; |
| 200 | + ctx.fillText("10:00 AM", trayX + trayW/2, trayY + 22*scale); |
| 201 | + |
| 202 | + // App Button on Taskbar (Active) |
| 203 | + const appW = 200 * scale; |
| 204 | + const appX = startX + startW + 10*scale; |
| 205 | + const appY = startY; |
| 206 | + |
| 207 | + // Pressed look |
| 208 | + drawBevel(appX, appY, appW, startH, true); |
| 209 | + ctx.fillStyle = black; |
| 210 | + ctx.textAlign = 'left'; |
| 211 | + ctx.fillText(`${repoName} - Notepad`, appX + 10*scale, appY + 22*scale); |
| 212 | +}; |
0 commit comments