Kutni PhotoShop-Samin Sami

by Samin Sami

HTML

<div class="app" id="app">
  <div class="titlebar">
    <div class="traffic"><span class="dot red"></span><span class="dot yellow"></span><span class="dot green"></span></div>
    <div class="app-title">Kutni Photoshop by Samin Sami</div>
    <div class="title-actions"><button class="share" data-action="share">Share</button><button class="title-icon" data-action="commandPalette" title="Search">⌕</button><button class="title-icon" data-action="togglePanels" title="Panels">▣</button><button class="title-icon" data-action="workspaceMenu" title="Workspace">⌄</button></div>
  </div>

  <div class="menubar" id="menubar">
    <div class="menu-item" data-menu="file">File<div class="dropdown">
      <div class="drop-row" data-action="newDoc"><span>New...</span><span class="shortcut">⌘N</span></div><div class="drop-row" data-action="open"><span>Open...</span><span class="shortcut">⌘O</span></div><div class="drop-row" data-action="openSample"><span>Open Sample Image</span></div><div class="drop-row" data-action="duplicateDoc"><span>Duplicate Document</span></div><div class="drop-sep"></div><div class="drop-row" data-action="save"><span>Save</span><span class="shortcut">⌘S</span></div><div class="drop-row" data-action="exportPng"><span>Export PNG...</span><span class="shortcut">⌥⇧⌘W</span></div><div class="drop-row" data-action="exportJpg"><span>Export JPG...</span></div><div class="drop-sep"></div><div class="drop-row" data-action="closeDoc"><span>Close</span><span class="shortcut">⌘W</span></div>
    </div></div>
    <div class="menu-item" data-menu="edit">Edit<div class="dropdown">
      <div class="drop-row" data-action="undo"><span>Undo</span><span class="shortcut">⌘Z</span></div><div class="drop-row" data-action="redo"><span>Redo</span><span class="shortcut">⇧⌘Z</span></div><div class="drop-sep"></div><div class="drop-row" data-action="copy"><span>Copy</span><span class="shortcut">⌘C</span></div><div class="drop-row" data-action="cut"><span>Cut</span><span...

CSS

:root{
  --bg:#242424;--chrome:#4c4c4c;--chrome2:#3f3f3f;--chrome3:#333;--panel:#3d3d3d;--panel2:#353535;--panel3:#2b2b2b;
  --line:#1d1d1d;--soft:#575757;--text:#dedede;--muted:#a8a8a8;--blue:#1473e6;--field:#2f2f2f;--fieldBorder:#686868;
  --menu:#efefef;--menuText:#222;--warn:#e0d484;
}
*{box-sizing:border-box}
html,body{margin:0;height:100%;background:#202020;color:var(--text);font:12px/1.22 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif;overflow:hidden;user-select:none}
button,input,select,textarea{font:inherit;color:inherit}
button{border:0;background:none;padding:0;cursor:default;color:inherit}
input,select,textarea{background:var(--field);border:1px solid var(--fieldBorder);border-radius:2px;color:#e7e7e7;outline:none}
input,select{height:21px;padding:0 5px} textarea{padding:6px;resize:none}
input[type="range"]{padding:0;accent-color:#a0a0a0} input[type="checkbox"]{height:auto;vertical-align:-2px;accent-color:#aaa} input[type="color"]{padding:0;width:28px;height:22px}
.app{height:100vh;display:grid;grid-template-rows:31px 26px 38px 1fr;background:var(--bg)}
.titlebar{display:grid;grid-template-columns:190px 1fr 260px;align-items:center;background:#525252;border-bottom:1px solid #292929;color:#e4e4e4}
.traffic{display:flex;gap:10px;align-items:center;padding-left:16px}.dot{width:13px;height:13px;border-radius:50%;box-shadow:inset 0 0 0 1px rgba(0,0,0,.28)}.red{background:#ff5f57}.yellow{background:#febc2e}.green{background:#28c840}
.app-title{text-align:center;font-size:15px;font-weight:650;text-shadow:0 1px 0 #111}.title-actions{display:flex;justify-content:flex-end;align-items:center;gap:10px;padding-right:12px}.share{background:#1473e6;color:white;border-radius:16px;padding:6px 17px;font-weight:650}.title-icon{font-size:17px;width:25px;height:25px;display:grid;place-items:center;border-radius:3px}.title-icon:hover{background:#5e5e5e}
.menubar{display:flex;align-items:center;background:#464646;border-bottom:1px solid...

JavaScript

(() => {
'use strict';
const $=(s,r=document)=>r.querySelector(s); const $$=(s,r=document)=>Array.from(r.querySelectorAll(s));
const mainCanvas=$('#mainCanvas'), overlayCanvas=$('#overlayCanvas'), workspace=$('#workspace'), stageShell=$('#stageShell');
const ctx=mainCanvas.getContext('2d',{willReadFrequently:true}); const octx=overlayCanvas.getContext('2d');
const selectionBox=$('#selectionBox'), cropBox=$('#cropBox');

const toolGroups=[
 {key:'V',tools:[['move','Move Tool','↕'],['artboard','Artboard Tool','▦']]},
 {key:'M',tools:[['marquee','Rectangular Marquee Tool','▭'],['ellipticalMarquee','Elliptical Marquee Tool','◯'],['singleRow','Single Row Marquee Tool','━'],['singleColumn','Single Column Marquee Tool','┃']]},
 {key:'L',tools:[['lasso','Lasso Tool','⌁'],['polygonalLasso','Polygonal Lasso Tool','⬠'],['magneticLasso','Magnetic Lasso Tool','⌁']]},
 {key:'W',tools:[['objectSelection','Object Selection Tool','▢'],['quickSelection','Quick Selection Tool','✣'],['magicWand','Magic Wand Tool','✦']]},
 {key:'C',tools:[['crop','Crop Tool','⌗'],['perspectiveCrop','Perspective Crop Tool','◫'],['slice','Slice Tool','⊞'],['sliceSelect','Slice Select Tool','▣']]},
 {key:'I',tools:[['eyedropper','Eyedropper Tool','i'],['colorSampler','Color Sampler Tool','⊙'],['ruler','Ruler Tool','╱'],['note','Note Tool','◫'],['count','Count Tool','#']]},
 {key:'J',tools:[['spotHeal','Spot Healing Brush Tool','✚'],['healingBrush','Healing Brush Tool','✚'],['patch','Patch Tool','▱'],['contentMove','Content-Aware Move Tool','↗'],['redEye','Red Eye Tool','◉']]},
 {key:'B',tools:[['brush','Brush Tool','●'],['pencil','Pencil Tool','✎'],['colorReplacement','Color Replacement Tool','◒'],['mixerBrush','Mixer Brush Tool','◍']]},
 {key:'S',tools:[['cloneStamp','Clone Stamp Tool','♟'],['patternStamp','Pattern Stamp Tool','▥']]},
 {key:'Y',tools:[['historyBrush','History Brush Tool','↶'],['artHistoryBrush','Art History Brush Tool','✺']]},
 {key:'E',tools:[['eraser','Eraser...