itacaiunas commited on
Commit
92abb9b
·
verified ·
1 Parent(s): bac6144

Crie um editor gráfico online semelhante ao Polotno Studio

Browse files

, com foco em design intuitivo, edição em tempo real e exportação de imagens.

O sistema deve permitir ao usuário:

Criar, importar e editar designs (imagens, textos, formas, ícones).

Manipular elementos via drag-and-drop, redimensionar, rotacionar, agrupar e alterar ordem (camadas).

Personalizar texto com escolha de fonte, tamanho, cor, alinhamento, espaçamento e sombra.

Adicionar imagens a partir do upload local ou de uma biblioteca gratuita (ex: Unsplash API opcional).

Adicionar formas e ícones vetoriais (SVG) com opção de mudar cor e opacidade.

Gerenciar múltiplas páginas ou quadros dentro de um mesmo projeto (como slides ou telas).

Salvar e carregar projetos no navegador (LocalStorage) e futuramente em banco de dados (ex: Firebase).

Exportar o design final como imagem (PNG, JPG) ou PDF em alta resolução.

Interface responsiva, moderna e leve, com layout limpo e fluido (estilo Canva/Polotno).

Modo escuro/claro automático e suporte a múltiplos idiomas (começando com PT-BR e EN).

⚙️ Requisitos técnicos sugeridos

Frontend: React + TypeScript + TailwindCSS + Zustand (ou Redux) para estado.

Canvas: Use Konva.js ou Fabric.js para manipulação gráfica 2D.

Backend (opcional): Node.js + Express ou Firebase Functions (para salvar projetos e uploads).

Armazenamento local: IndexedDB ou LocalStorage para rascunhos automáticos.

Exportação: Utilize html-to-image ou konva.toDataURL() para geração de PNG/PDF.

UI Components: ShadCN/UI ou Radix UI para painéis laterais, sliders e botões.

Design:

Painel lateral esquerdo: elementos (textos, imagens, formas, templates).

Painel lateral direito: propriedades e estilos do item selecionado.

Barra superior: novo projeto, desfazer/refazer, salvar, exportar.

Área central: canvas editável com zoom e guias de alinhamento.

🚀 Funcionalidades extras (para roadmap futuro)

Camadas com visibilidade/lock.

Upload e remoção de fontes customizadas.

Templates salvos.

Colaboração em tempo real (WebSocket).

Geração de imagens com IA (ex: via API de imagem).

💡 Exemplo de instrução curta alternativa (para versão resumida):

Crie um editor de design online tipo Polotno Studio, com React + Tailwind + Konva.js.
Deve permitir adicionar texto, imagens e formas, editar estilo, mover via drag-and-drop, salvar no navegador e exportar como PNG/PDF.
Interface moderna e responsiva, com painéis laterais para controle dos elementos e estilo.

Files changed (7) hide show
  1. README.md +8 -5
  2. components/navbar.js +113 -0
  3. components/properties-panel.js +1 -0
  4. components/toolbar.js +173 -0
  5. index.html +58 -19
  6. script.js +122 -0
  7. style.css +79 -19
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Designcraft Studio
3
- emoji: 🌖
4
- colorFrom: indigo
5
- colorTo: green
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: DesignCraft Studio ✨🎨
3
+ colorFrom: green
4
+ colorTo: red
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
components/navbar.js ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomNavbar extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ .navbar {
7
+ height: 4rem;
8
+ background-color: var(--primary);
9
+ color: white;
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: space-between;
13
+ padding: 0 1.5rem;
14
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
15
+ }
16
+
17
+ .logo {
18
+ font-weight: 600;
19
+ font-size: 1.25rem;
20
+ display: flex;
21
+ align-items: center;
22
+ gap: 0.5rem;
23
+ }
24
+
25
+ .actions {
26
+ display: flex;
27
+ gap: 1rem;
28
+ }
29
+
30
+ .btn {
31
+ padding: 0.5rem 1rem;
32
+ border-radius: 0.375rem;
33
+ font-weight: 500;
34
+ cursor: pointer;
35
+ display: flex;
36
+ align-items: center;
37
+ gap: 0.5rem;
38
+ transition: all 0.2s;
39
+ }
40
+
41
+ .btn-primary {
42
+ background-color: white;
43
+ color: var(--primary);
44
+ }
45
+
46
+ .btn-primary:hover {
47
+ background-color: #f3f4f6;
48
+ }
49
+
50
+ .btn-secondary {
51
+ background-color: rgba(255, 255, 255, 0.1);
52
+ color: white;
53
+ }
54
+
55
+ .btn-secondary:hover {
56
+ background-color: rgba(255, 255, 255, 0.2);
57
+ }
58
+
59
+ .icon {
60
+ width: 1.25rem;
61
+ height: 1.25rem;
62
+ }
63
+ </style>
64
+
65
+ <nav class="navbar">
66
+ <div class="logo">
67
+ <i data-feather="pen-tool" class="icon"></i>
68
+ <span>DesignCraft Studio</span>
69
+ </div>
70
+
71
+ <div class="actions">
72
+ <button class="btn btn-secondary" id="save-btn" tooltip="Salvar (Ctrl+S)">
73
+ <i data-feather="save" class="icon"></i>
74
+ <span class="hidden md:inline">Salvar</span>
75
+ </button>
76
+ <button class="btn btn-secondary" id="undo-btn" tooltip="Desfazer (Ctrl+Z)">
77
+ <i data-feather="rotate-ccw" class="icon"></i>
78
+ </button>
79
+ <button class="btn btn-secondary" id="redo-btn" tooltip="Refazer (Ctrl+Y)">
80
+ <i data-feather="rotate-cw" class="icon"></i>
81
+ </button>
82
+ <button class="btn btn-primary" id="export-btn" tooltip="Exportar (Ctrl+E)">
83
+ <i data-feather="download" class="icon"></i>
84
+ <span class="hidden md:inline">Exportar</span>
85
+ </button>
86
+ </div>
87
+ </nav>
88
+ `;
89
+
90
+ // Add event listeners
91
+ this.shadowRoot.getElementById('save-btn').addEventListener('click', () => {
92
+ saveProject();
93
+ this.showToast('Projeto salvo com sucesso!');
94
+ });
95
+
96
+ this.shadowRoot.getElementById('export-btn').addEventListener('click', () => {
97
+ document.querySelector('custom-export-modal').setAttribute('open', 'true');
98
+ });
99
+ }
100
+
101
+ showToast(message) {
102
+ const toast = document.createElement('div');
103
+ toast.className = 'fixed bottom-4 right-4 bg-green-500 text-white px-4 py-2 rounded-md shadow-lg';
104
+ toast.textContent = message;
105
+ document.body.appendChild(toast);
106
+
107
+ setTimeout(() => {
108
+ toast.remove();
109
+ }, 3000);
110
+ }
111
+ }
112
+
113
+ customElements.define('custom-navbar', CustomNavbar);
components/properties-panel.js ADDED
@@ -0,0 +1 @@
 
 
1
+ class CustomPropertiesPanel extends
components/toolbar.js ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomToolbar extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ .toolbar {
7
+ width: 4rem;
8
+ background-color: white;
9
+ border-right: 1px solid #e5e7eb;
10
+ display: flex;
11
+ flex-direction: column;
12
+ align-items: center;
13
+ padding: 1rem 0;
14
+ gap: 0.5rem;
15
+ overflow-y: auto;
16
+ }
17
+
18
+ .dark .toolbar {
19
+ background-color: #1f2937;
20
+ border-right-color: #374151;
21
+ }
22
+
23
+ .tool-btn {
24
+ width: 2.5rem;
25
+ height: 2.5rem;
26
+ border-radius: 0.375rem;
27
+ display: flex;
28
+ align-items: center;
29
+ justify-content: center;
30
+ cursor: pointer;
31
+ transition: all 0.2s;
32
+ color: #4b5563;
33
+ }
34
+
35
+ .dark .tool-btn {
36
+ color: #9ca3af;
37
+ }
38
+
39
+ .tool-btn:hover {
40
+ background-color: #e5e7eb;
41
+ color: #1f2937;
42
+ }
43
+
44
+ .dark .tool-btn:hover {
45
+ background-color: #374151;
46
+ color: #f3f4f6;
47
+ }
48
+
49
+ .tool-btn.active {
50
+ background-color: var(--primary-light);
51
+ color: white;
52
+ }
53
+
54
+ .dark .tool-btn.active {
55
+ background-color: var(--primary-dark);
56
+ }
57
+
58
+ .tool-separator {
59
+ width: 80%;
60
+ height: 1px;
61
+ background-color: #e5e7eb;
62
+ margin: 0.5rem 0;
63
+ }
64
+
65
+ .dark .tool-separator {
66
+ background-color: #374151;
67
+ }
68
+
69
+ .icon {
70
+ width: 1.25rem;
71
+ height: 1.25rem;
72
+ }
73
+ </style>
74
+
75
+ <div class="toolbar">
76
+ <button class="tool-btn" id="select-tool" tooltip="Selecionar (V)">
77
+ <i data-feather="mouse-pointer" class="icon"></i>
78
+ </button>
79
+
80
+ <button class="tool-btn" id="text-tool" tooltip="Texto (T)">
81
+ <i data-feather="type" class="icon"></i>
82
+ </button>
83
+
84
+ <button class="tool-btn" id="image-tool" tooltip="Imagem (I)">
85
+ <i data-feather="image" class="icon"></i>
86
+ </button>
87
+
88
+ <div class="tool-separator"></div>
89
+
90
+ <button class="tool-btn" id="rectangle-tool" tooltip="Retângulo (R)">
91
+ <i data-feather="square" class="icon"></i>
92
+ </button>
93
+
94
+ <button class="tool-btn" id="circle-tool" tooltip="Círculo (C)">
95
+ <i data-feather="circle" class="icon"></i>
96
+ </button>
97
+
98
+ <button class="tool-btn" id="line-tool" tooltip="Linha (L)">
99
+ <i data-feather="minus" class="icon"></i>
100
+ </button>
101
+
102
+ <div class="tool-separator"></div>
103
+
104
+ <button class="tool-btn" id="undo-btn" tooltip="Desfazer (Ctrl+Z)">
105
+ <i data-feather="rotate-ccw" class="icon"></i>
106
+ </button>
107
+
108
+ <button class="tool-btn" id="redo-btn" tooltip="Refazer (Ctrl+Y)">
109
+ <i data-feather="rotate-cw" class="icon"></i>
110
+ </button>
111
+
112
+ <div class="tool-separator"></div>
113
+
114
+ <button class="tool-btn" id="zoom-in-btn" tooltip="Zoom In (+)">
115
+ <i data-feather="zoom-in" class="icon"></i>
116
+ </button>
117
+
118
+ <button class="tool-btn" id="zoom-out-btn" tooltip="Zoom Out (-)">
119
+ <i data-feather="zoom-out" class="icon"></i>
120
+ </button>
121
+ </div>
122
+ `;
123
+
124
+ // Add event listeners for tools
125
+ this.shadowRoot.getElementById('text-tool').addEventListener('click', () => {
126
+ this.setActiveTool('text-tool');
127
+ this.addTextElement();
128
+ });
129
+ }
130
+
131
+ setActiveTool(toolId) {
132
+ const tools = this.shadowRoot.querySelectorAll('.tool-btn');
133
+ tools.forEach(tool => {
134
+ tool.classList.remove('active');
135
+ });
136
+
137
+ const activeTool = this.shadowRoot.getElementById(toolId);
138
+ if (activeTool) {
139
+ activeTool.classList.add('active');
140
+ }
141
+ }
142
+
143
+ addTextElement() {
144
+ const stage = window.designStage;
145
+ const layer = window.designLayer;
146
+
147
+ const text = new Konva.Text({
148
+ x: 50,
149
+ y: 50,
150
+ text: 'Clique para editar',
151
+ fontSize: 20,
152
+ fontFamily: 'Inter',
153
+ fill: '#000000',
154
+ draggable: true
155
+ });
156
+
157
+ layer.add(text);
158
+ stage.add(layer);
159
+
160
+ // Add to state
161
+ state.elements.push({
162
+ type: 'text',
163
+ x: 50,
164
+ y: 50,
165
+ text: 'Clique para editar',
166
+ fontSize: 20,
167
+ fontFamily: 'Inter',
168
+ color: '#000000'
169
+ });
170
+ }
171
+ }
172
+
173
+ customElements.define('custom-toolbar', CustomToolbar);
index.html CHANGED
@@ -1,19 +1,58 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="pt-BR">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>DesignCraft Studio - Editor Gráfico Online</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
10
+ <script src="https://unpkg.com/feather-icons"></script>
11
+ <script src="https://unpkg.com/konva@8.3.2/konva.min.js"></script>
12
+ </head>
13
+ <body class="bg-gray-50 dark:bg-gray-900 transition-colors duration-200">
14
+ <custom-navbar></custom-navbar>
15
+ <main class="flex h-[calc(100vh-4rem)]">
16
+ <custom-toolbar></custom-toolbar>
17
+ <div class="flex-1 relative overflow-hidden">
18
+ <div id="canvas-container" class="absolute inset-0 flex items-center justify-center bg-gray-100 dark:bg-gray-800">
19
+ <div id="stage-container" class="shadow-xl"></div>
20
+ </div>
21
+ </div>
22
+ <custom-properties-panel></custom-properties-panel>
23
+ </main>
24
+ <custom-footer></custom-footer>
25
+
26
+ <!-- Modals -->
27
+ <custom-image-upload-modal></custom-image-upload-modal>
28
+ <custom-export-modal></custom-export-modal>
29
+
30
+ <!-- Components -->
31
+ <script src="components/navbar.js"></script>
32
+ <script src="components/toolbar.js"></script>
33
+ <script src="components/properties-panel.js"></script>
34
+ <script src="components/footer.js"></script>
35
+ <script src="components/image-upload-modal.js"></script>
36
+ <script src="components/export-modal.js"></script>
37
+
38
+ <script src="script.js"></script>
39
+ <script>
40
+ feather.replace();
41
+ // Initialize Konva stage
42
+ document.addEventListener('DOMContentLoaded', () => {
43
+ const stage = new Konva.Stage({
44
+ container: 'stage-container',
45
+ width: 800,
46
+ height: 600
47
+ });
48
+ const layer = new Konva.Layer();
49
+ stage.add(layer);
50
+
51
+ // Store stage reference
52
+ window.designStage = stage;
53
+ window.designLayer = layer;
54
+ });
55
+ </script>
56
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
57
+ </body>
58
+ </html>
script.js ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Global state management
2
+ const state = {
3
+ currentProject: null,
4
+ selectedElement: null,
5
+ elements: [],
6
+ history: [],
7
+ historyIndex: -1,
8
+ canvasSize: { width: 800, height: 600 },
9
+ colorPalette: ['#6366F1', '#F59E0B', '#10B981', '#EF4444', '#3B82F6', '#8B5CF6', '#EC4899'],
10
+ fonts: ['Inter', 'Arial', 'Helvetica', 'Times New Roman', 'Courier New', 'Georgia', 'Verdana'],
11
+ darkMode: window.matchMedia('(prefers-color-scheme: dark)').matches
12
+ };
13
+
14
+ // Initialize dark mode
15
+ function initDarkMode() {
16
+ if (state.darkMode) {
17
+ document.documentElement.classList.add('dark');
18
+ } else {
19
+ document.documentElement.classList.remove('dark');
20
+ }
21
+ }
22
+
23
+ // Save project to localStorage
24
+ function saveProject() {
25
+ if (state.currentProject) {
26
+ const projectData = {
27
+ id: state.currentProject.id,
28
+ name: state.currentProject.name,
29
+ elements: state.elements,
30
+ canvasSize: state.canvasSize,
31
+ createdAt: new Date().toISOString(),
32
+ updatedAt: new Date().toISOString()
33
+ };
34
+ localStorage.setItem(`project_${state.currentProject.id}`, JSON.stringify(projectData));
35
+ }
36
+ }
37
+
38
+ // Load project from localStorage
39
+ function loadProject(projectId) {
40
+ const projectData = localStorage.getItem(`project_${projectId}`);
41
+ if (projectData) {
42
+ const parsedData = JSON.parse(projectData);
43
+ state.currentProject = {
44
+ id: parsedData.id,
45
+ name: parsedData.name,
46
+ createdAt: parsedData.createdAt,
47
+ updatedAt: parsedData.updatedAt
48
+ };
49
+ state.elements = parsedData.elements || [];
50
+ state.canvasSize = parsedData.canvasSize || { width: 800, height: 600 };
51
+
52
+ // Render elements on canvas
53
+ renderElements();
54
+ }
55
+ }
56
+
57
+ // Render elements on canvas
58
+ function renderElements() {
59
+ const stage = window.designStage;
60
+ const layer = window.designLayer;
61
+
62
+ // Clear existing elements
63
+ layer.destroyChildren();
64
+
65
+ // Set canvas size
66
+ stage.width(state.canvasSize.width);
67
+ stage.height(state.canvasSize.height);
68
+
69
+ // Render each element
70
+ state.elements.forEach(element => {
71
+ let konvaElement;
72
+
73
+ switch (element.type) {
74
+ case 'text':
75
+ konvaElement = new Konva.Text({
76
+ x: element.x,
77
+ y: element.y,
78
+ text: element.text,
79
+ fontSize: element.fontSize,
80
+ fontFamily: element.fontFamily,
81
+ fill: element.color,
82
+ align: element.align,
83
+ width: element.width,
84
+ draggable: true
85
+ });
86
+ break;
87
+ case 'image':
88
+ // Image rendering logic
89
+ break;
90
+ case 'shape':
91
+ // Shape rendering logic
92
+ break;
93
+ }
94
+
95
+ if (konvaElement) {
96
+ layer.add(konvaElement);
97
+ }
98
+ });
99
+
100
+ stage.add(layer);
101
+ }
102
+
103
+ // Initialize app
104
+ document.addEventListener('DOMContentLoaded', () => {
105
+ initDarkMode();
106
+
107
+ // Listen for dark mode changes
108
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
109
+ state.darkMode = e.matches;
110
+ initDarkMode();
111
+ });
112
+
113
+ // Create new project if none exists
114
+ if (!state.currentProject) {
115
+ state.currentProject = {
116
+ id: Date.now().toString(),
117
+ name: 'Novo Projeto',
118
+ createdAt: new Date().toISOString(),
119
+ updatedAt: new Date().toISOString()
120
+ };
121
+ }
122
+ });
style.css CHANGED
@@ -1,28 +1,88 @@
1
- body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
2
+
3
+ :root {
4
+ --primary: #6366f1;
5
+ --primary-light: #818cf8;
6
+ --primary-dark: #4f46e5;
7
+ --secondary: #f59e0b;
8
+ --secondary-light: #fbbf24;
9
+ --secondary-dark: #d97706;
10
+ }
11
+
12
+ * {
13
+ font-family: 'Inter', sans-serif;
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ /* Scrollbar styling */
18
+ ::-webkit-scrollbar {
19
+ width: 8px;
20
+ height: 8px;
21
+ }
22
+
23
+ ::-webkit-scrollbar-track {
24
+ background: #f1f1f1;
25
+ }
26
+
27
+ ::-webkit-scrollbar-thumb {
28
+ background: #c1c1c1;
29
+ border-radius: 4px;
30
+ }
31
+
32
+ ::-webkit-scrollbar-thumb:hover {
33
+ background: #a1a1a1;
34
+ }
35
+
36
+ .dark ::-webkit-scrollbar-track {
37
+ background: #2d3748;
38
+ }
39
+
40
+ .dark ::-webkit-scrollbar-thumb {
41
+ background: #4a5568;
42
  }
43
 
44
+ .dark ::-webkit-scrollbar-thumb:hover {
45
+ background: #718096;
 
46
  }
47
 
48
+ /* Konva canvas styling */
49
+ #stage-container {
50
+ background-color: white;
51
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
 
52
  }
53
 
54
+ .dark #stage-container {
55
+ background-color: #1f2937;
 
 
 
 
56
  }
57
 
58
+ /* Tooltip styling */
59
+ [tooltip] {
60
+ position: relative;
61
  }
62
+
63
+ [tooltip]::before {
64
+ content: attr(tooltip);
65
+ position: absolute;
66
+ bottom: 100%;
67
+ left: 50%;
68
+ transform: translateX(-50%);
69
+ padding: 0.25rem 0.5rem;
70
+ background-color: #333;
71
+ color: white;
72
+ border-radius: 0.25rem;
73
+ font-size: 0.75rem;
74
+ white-space: nowrap;
75
+ opacity: 0;
76
+ pointer-events: none;
77
+ transition: opacity 0.2s;
78
+ z-index: 50;
79
+ }
80
+
81
+ [tooltip]:hover::before {
82
+ opacity: 1;
83
+ }
84
+
85
+ .dark [tooltip]::before {
86
+ background-color: #f3f4f6;
87
+ color: #111827;
88
+ }