diff --git a/apps/client/public/locales/en/translation.json b/apps/client/public/locales/en/translation.json index ec8bd5e..baa4ebf 100644 --- a/apps/client/public/locales/en/translation.json +++ b/apps/client/public/locales/en/translation.json @@ -149,6 +149,8 @@ "toolbar.elec": "Elec", "toolbar.furn": "Furn", "toolbar.meas": "Meas", + "toolbar.stretchCeiling": "Ceiling", + "toolbar.toggleStretchCeiling": "Toggle stretch ceiling overlay", "toolbar.toggleGrid": "Toggle grid", "toolbar.toggleSnap": "Toggle snap", "toolbar.toggleWalls": "Toggle walls layer", @@ -222,6 +224,7 @@ "floor.WOOD_MEDIUM": "Medium Wood", "floor.WOOD_DARK": "Dark Wood", "floor.WOOD_HERRINGBONE": "Herringbone", + "floor.OAK_NATURAL": "Natural Oak", "floor.TILE_WHITE": "White Tile", "floor.TILE_GRAY": "Gray Tile", "floor.LAMINATE": "Laminate", @@ -242,6 +245,28 @@ "properties.outletWidth": "Outlet width", "properties.outletHeight": "Outlet height", "properties.outletCount": "Count", + "properties.outletDirectionLabel": "Direction", + "properties.outletDirection.horizontal": "Horizontal", + "properties.outletDirection.vertical": "Vertical", + "properties.stretchCeilingOffset": "Stretch ceiling drop", + "properties.wallLightStyleLabel": "Style", + "properties.wallLightStyle.classic": "Classic", + "properties.wallLightStyle.pendant-globe": "Pendant Globe", + "properties.wallLightStyle.sconce-up": "Sconce Up", + "properties.wallLightStyle.sconce-down": "Sconce Down", + "properties.cordLength": "Cord length", + "properties.lampSize": "Lamp size", + "properties.surfaceTexture": "Surface", + "furnitureTexture.NONE": "None (solid color)", + "furnitureTexture.WOOD_LIGHT": "Light Wood", + "furnitureTexture.WOOD_MEDIUM": "Medium Wood", + "furnitureTexture.WOOD_DARK": "Dark Wood", + "furnitureTexture.WOOD_HERRINGBONE": "Herringbone", + "furnitureTexture.OAK_NATURAL": "Natural Oak", + "furnitureTexture.LAMINATE": "Laminate", + "furnitureTexture.CONCRETE": "Concrete", + "properties.invertCoordX": "Invert X display", + "properties.invertCoordY": "Invert Y display", "properties.anchor": "Anchor", "anchor.left": "Left", "anchor.middle": "Middle", diff --git a/apps/client/public/locales/ru/translation.json b/apps/client/public/locales/ru/translation.json index c0ebbc2..071a5e6 100644 --- a/apps/client/public/locales/ru/translation.json +++ b/apps/client/public/locales/ru/translation.json @@ -152,6 +152,8 @@ "toolbar.elec": "Элек", "toolbar.furn": "Мебель", "toolbar.meas": "Разм", + "toolbar.stretchCeiling": "Потолок", + "toolbar.toggleStretchCeiling": "Показать/скрыть натяжной потолок", "toolbar.toggleGrid": "Переключить сетку", "toolbar.toggleSnap": "Переключить привязку", "toolbar.toggleWalls": "Переключить слой стен", @@ -225,6 +227,7 @@ "floor.WOOD_MEDIUM": "Среднее дерево", "floor.WOOD_DARK": "Тёмное дерево", "floor.WOOD_HERRINGBONE": "Ёлочка", + "floor.OAK_NATURAL": "Натуральный дуб", "floor.TILE_WHITE": "Белая плитка", "floor.TILE_GRAY": "Серая плитка", "floor.LAMINATE": "Ламинат", @@ -245,6 +248,28 @@ "properties.outletWidth": "Ширина розетки", "properties.outletHeight": "Высота розетки", "properties.outletCount": "Количество", + "properties.outletDirectionLabel": "Направление", + "properties.outletDirection.horizontal": "Горизонтально", + "properties.outletDirection.vertical": "Вертикально", + "properties.stretchCeilingOffset": "Натяжной потолок (отступ)", + "properties.wallLightStyleLabel": "Стиль", + "properties.wallLightStyle.classic": "Классический", + "properties.wallLightStyle.pendant-globe": "Подвесной шар", + "properties.wallLightStyle.sconce-up": "Бра вверх", + "properties.wallLightStyle.sconce-down": "Бра вниз", + "properties.cordLength": "Длина шнура", + "properties.lampSize": "Размер светильника", + "properties.surfaceTexture": "Поверхность", + "furnitureTexture.NONE": "Нет (сплошной цвет)", + "furnitureTexture.WOOD_LIGHT": "Светлое дерево", + "furnitureTexture.WOOD_MEDIUM": "Среднее дерево", + "furnitureTexture.WOOD_DARK": "Тёмное дерево", + "furnitureTexture.WOOD_HERRINGBONE": "Ёлочка", + "furnitureTexture.OAK_NATURAL": "Натуральный дуб", + "furnitureTexture.LAMINATE": "Ламинат", + "furnitureTexture.CONCRETE": "Бетон", + "properties.invertCoordX": "Инвертировать X", + "properties.invertCoordY": "Инвертировать Y", "properties.anchor": "Привязка", "anchor.left": "Слева", "anchor.middle": "По центру", diff --git a/apps/client/public/textures/floors/oak_natural/color.jpg b/apps/client/public/textures/floors/oak_natural/color.jpg new file mode 100644 index 0000000..5a7f7b4 Binary files /dev/null and b/apps/client/public/textures/floors/oak_natural/color.jpg differ diff --git a/apps/client/public/textures/floors/oak_natural/normal.jpg b/apps/client/public/textures/floors/oak_natural/normal.jpg new file mode 100644 index 0000000..8f439a2 Binary files /dev/null and b/apps/client/public/textures/floors/oak_natural/normal.jpg differ diff --git a/apps/client/public/textures/floors/oak_natural/roughness.jpg b/apps/client/public/textures/floors/oak_natural/roughness.jpg new file mode 100644 index 0000000..0442169 Binary files /dev/null and b/apps/client/public/textures/floors/oak_natural/roughness.jpg differ diff --git a/apps/client/src/components/editor/EditorCanvas.tsx b/apps/client/src/components/editor/EditorCanvas.tsx index 69ff698..98f9aa6 100644 --- a/apps/client/src/components/editor/EditorCanvas.tsx +++ b/apps/client/src/components/editor/EditorCanvas.tsx @@ -28,6 +28,7 @@ import { computeFurniturePreview, createFurnitureItemFromPlacement } from './too import { startMeasurement, updateMeasurement, finishMeasurement } from './tools/MeasureTool'; import { ELECTRICAL_SYMBOL_DEFS } from './symbols/electrical'; import { FURNITURE_DEFS } from './symbols/furniture'; +import { useCanvasColors } from './utils/canvasThemeColors'; import { AnnotationLayer } from './layers/AnnotationLayer'; import { MeasureOverlayLayer } from './layers/MeasureOverlayLayer'; import { generateLocalId } from './utils/geometry'; @@ -53,6 +54,7 @@ const TOOL_CURSORS: Record = { export function EditorCanvas({ width, height, onStageRef }: EditorCanvasProps) { const { t } = useTranslation(); + const canvasColors = useCanvasColors(); const { zoom, panOffset, setZoom, setPanOffset } = useZoomPan(); const { selectedIds, @@ -169,7 +171,11 @@ export function EditorCanvas({ width, height, onStageRef }: EditorCanvasProps) { const worldPoint = getWorldPoint(e); if (activeTool === 'select') { - const hit = hitTest(worldPoint, openings, walls, electricalItems, furnitureItems); + // When furniture is (nearly) fully transparent it's effectively + // invisible — skip it in hit-testing so clicks fall through to + // elements underneath instead of selecting a ghost. + const hittableFurniture = globalFurnitureOpacity < 0.05 ? [] : furnitureItems; + const hit = hitTest(worldPoint, openings, walls, electricalItems, hittableFurniture); if (hit) { if (e.evt.shiftKey) { @@ -464,7 +470,9 @@ export function EditorCanvas({ width, height, onStageRef }: EditorCanvasProps) { // Only if the rectangle is non-trivial if (rect.width > 0.01 || rect.height > 0.01) { - const ids = elementsInRect(rect, openings, walls, electricalItems, furnitureItems); + // Skip furniture when globally transparent — see hit-test above. + const selectableFurniture = globalFurnitureOpacity < 0.05 ? [] : furnitureItems; + const ids = elementsInRect(rect, openings, walls, electricalItems, selectableFurniture); if (ids.size > 0) { selectionDispatch({ type: 'SET_SELECTED', ids }); } @@ -509,7 +517,7 @@ export function EditorCanvas({ width, height, onStageRef }: EditorCanvasProps) { onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} - style={{ cursor, background: '#ffffff' }} + style={{ cursor, background: canvasColors.canvasBg }} > {/* Konva renders one HTML per ; performance recommends 3-5 diff --git a/apps/client/src/components/editor/EditorToolbar.tsx b/apps/client/src/components/editor/EditorToolbar.tsx index 0f3814d..c093d20 100644 --- a/apps/client/src/components/editor/EditorToolbar.tsx +++ b/apps/client/src/components/editor/EditorToolbar.tsx @@ -193,6 +193,16 @@ export function EditorToolbar({ onSave, isSaving, onExport, onImport }: EditorTo > {t('toolbar.meas')} +