From 925da2047c45f0a819559b329594195e56f8452a Mon Sep 17 00:00:00 2001 From: Denis 'GeneralGDA' Gladkiy Date: Tue, 21 Oct 2025 11:05:40 +0600 Subject: [PATCH 1/7] Fix: reduced theme blinking on desktop app start (windows). (#10119) Signed-off-by: Denis Gladkiy --- desktop/src/ui/index.ejs | 17 ++++++++++++++++- desktop/src/ui/index.ts | 13 ++++++++----- desktop/src/ui/titleBarMenu.ts | 23 +++++++++++++++-------- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/desktop/src/ui/index.ejs b/desktop/src/ui/index.ejs index ae893ba49a..5560107cc9 100644 --- a/desktop/src/ui/index.ejs +++ b/desktop/src/ui/index.ejs @@ -24,7 +24,12 @@ box-sizing: border-box; } + body { + background-color: var(--bg-body, #ffffff); + } + :root { + --bg-body: #ffffff; --bg-secondary: #f3f3f3; --bg-tertiary: #ffffff; --bg-hover: #e8e8e8; @@ -39,6 +44,7 @@ } [data-theme="theme-dark"] { + --bg-body: #1a1928; --bg-secondary: #323233; --bg-tertiary: #252526; --bg-hover: #2a2d2e; @@ -236,4 +242,13 @@ - \ No newline at end of file + + +<% if (typeof isWindows !== 'undefined' && isWindows) { %> + +<% } %> \ No newline at end of file diff --git a/desktop/src/ui/index.ts b/desktop/src/ui/index.ts index 6aa383ed5c..93306f1014 100644 --- a/desktop/src/ui/index.ts +++ b/desktop/src/ui/index.ts @@ -25,6 +25,7 @@ import { closePopup, createApp, getCurrentResolvedLocation, + Menu, navigate, parseLocation, pushRootBarProgressComponent, @@ -82,11 +83,13 @@ window.addEventListener('DOMContentLoaded', () => { } }) - void ipcMain.isOsUsingDarkTheme().then((isDarkTheme) => { - menuBar.setTheme(isDarkTheme ? ThemeVariant.Dark : ThemeVariant.Light) - }).catch(() => { - menuBar.setTheme(ThemeVariant.Light) // fallback - }) + if (menuBar.lastUsedThemeIsUnknown()) { + void ipcMain.isOsUsingDarkTheme().then((isDarkTheme) => { + menuBar.setTheme(isDarkTheme ? ThemeVariant.Dark : ThemeVariant.Light) + }).catch(() => { + menuBar.setTheme(ThemeVariant.Light) // fallback + }) + } }) } } diff --git a/desktop/src/ui/titleBarMenu.ts b/desktop/src/ui/titleBarMenu.ts index 43847b2d67..32179830a3 100644 --- a/desktop/src/ui/titleBarMenu.ts +++ b/desktop/src/ui/titleBarMenu.ts @@ -16,7 +16,7 @@ import { IPCMainExposed, MenuBarAction } from './types' import { isMenuBarAction } from './typesUtils' import { TitleBarMenuState } from './titleBarMenuState' -import { ThemeVariant, type ThemeVariantType } from '@hcengineering/theme' +import { type ThemeVariantType } from '@hcengineering/theme' const ToggleMinimizeToTrayAction: MenuBarAction = 'toggle-minimize-to-tray' const ToggleAutoLaunchAction: MenuBarAction = 'toggle-auto-launch' @@ -28,7 +28,7 @@ const LabelAutoLaunchEnabled = '☑ Launch at Login' const LabelAutoLaunchDisabled = '☐ Launch at Login' export async function setupTitleBarMenu (ipcMain: IPCMainExposed, root: HTMLElement): Promise { - const themeManager = new ThemeManager(ThemeVariant.Light) + const themeManager = new ThemeManager() const minimizeToTrayEnabled = await ipcMain.isMinimizeToTrayEnabled() const isAutoLaunchEnabled = await ipcMain.isAutoLaunchEnabled() const menuManager = new MenuBarManager(root, minimizeToTrayEnabled, isAutoLaunchEnabled) @@ -75,12 +75,19 @@ export async function setupTitleBarMenu (ipcMain: IPCMainExposed, root: HTMLElem } export class MenuBar { + private static readonly lastUsedThemeKey = 'last-used-theme' + constructor (private readonly theme: ThemeManager) { } public setTheme (theme: ThemeVariantType): void { + localStorage.setItem(MenuBar.lastUsedThemeKey, theme) this.theme.setTheme(theme) } + + public lastUsedThemeIsUnknown (): boolean { + return localStorage.getItem(MenuBar.lastUsedThemeKey) == null + } } export function buildHulyApplicationMenu (minimizeToTrayEnabled: boolean, autoLaunchEnabled: boolean): HTMLElement { @@ -130,14 +137,14 @@ export function buildHulyApplicationMenu (minimizeToTrayEnabled: boolean, autoLa } class ThemeManager { - private readonly domThemeKey = 'data-theme' - - constructor (theme: ThemeVariantType) { - this.setTheme(theme) - } + private static readonly domThemeKey = 'data-theme' public setTheme (theme: ThemeVariantType): void { - document.body.setAttribute(this.domThemeKey, theme) + ThemeManager.setTheme(theme) + } + + public static setTheme (theme: ThemeVariantType): void { + document.body.setAttribute(ThemeManager.domThemeKey, theme) } } From 0ee7e4ed6dece9a515fd5a2863e0dcf1296d0f61 Mon Sep 17 00:00:00 2001 From: Denis 'GeneralGDA' Gladkiy Date: Tue, 21 Oct 2025 19:47:03 +0600 Subject: [PATCH 2/7] Drawing board control refinement (#10121) * Drawing toolbar controls switching: return pen, after panning and color selection. Signed-off-by: Denis Gladkiy * Middle mouse button panning. Signed-off-by: Denis Gladkiy --------- Signed-off-by: Denis Gladkiy --- .../src/___tests___/drawing.test.ts | 49 ++++++++++++ .../src/components/DrawingBoard.svelte | 4 + packages/presentation/src/drawing.ts | 77 +++++++++++++++---- .../src/components/DrawingBoardEditor.svelte | 5 +- 4 files changed, 117 insertions(+), 18 deletions(-) diff --git a/packages/presentation/src/___tests___/drawing.test.ts b/packages/presentation/src/___tests___/drawing.test.ts index 703cfe5fb9..14ed0a25ba 100644 --- a/packages/presentation/src/___tests___/drawing.test.ts +++ b/packages/presentation/src/___tests___/drawing.test.ts @@ -593,5 +593,54 @@ describe('drawing module tests', () => { ]) }) }) + + describe('tools selection', () => { + it('select color after panning', () => { + const colorsList: ColorsList = [ + ['alpha', new ThemeAwareColor('red', 'yellow')], + ['beta', new ThemeAwareColor('blue', 'blue')] + ] + + const toolChangedSpy = jest.fn() + + const initialPenColor: ColorMetaNameOrHex = 'alpha' + const systemUnderTest = drawing(drawingPlugInPoint, { + colorsList, + getCurrentTheme: () => 'theme-light', + subscribeOnThemeChange: () => {}, + readonly: false, + imageWidth: 400, + imageHeight: 300, + commands: [], + tool: 'pen', + penColor: initialPenColor, + toolChanged: toolChangedSpy + }) + + systemUnderTest.update?.({ + colorsList, + getCurrentTheme: () => 'theme-light', + subscribeOnThemeChange: () => {}, + readonly: false, + tool: 'pan', + toolChanged: toolChangedSpy + }) + + expect(toolChangedSpy).toHaveBeenCalledWith('pan') + toolChangedSpy.mockClear() + + const newColor: ColorMetaNameOrHex = 'beta' + systemUnderTest.update?.({ + colorsList, + getCurrentTheme: () => 'theme-light', + subscribeOnThemeChange: () => {}, + readonly: false, + penColor: newColor, + toolChanged: toolChangedSpy + }) + + expect(toolChangedSpy).toHaveBeenCalledWith('pen') + }) + }) }) }) diff --git a/packages/presentation/src/components/DrawingBoard.svelte b/packages/presentation/src/components/DrawingBoard.svelte index fe64e01704..d7cba893a4 100644 --- a/packages/presentation/src/components/DrawingBoard.svelte +++ b/packages/presentation/src/components/DrawingBoard.svelte @@ -230,6 +230,7 @@ penWidth, eraserWidth, fontSize, + enableMiddleMousePanning: false, changingCmdId, cmdAdded: addCommand, cmdChanging: showCommandProps, @@ -240,6 +241,9 @@ cmdDeleted: deleteCommand, editorCreated: (editor) => { cmdEditor = editor + }, + toolChanged: (newTool) => { + tool = newTool } }} > diff --git a/packages/presentation/src/drawing.ts b/packages/presentation/src/drawing.ts index f788163f08..8573286654 100644 --- a/packages/presentation/src/drawing.ts +++ b/packages/presentation/src/drawing.ts @@ -57,6 +57,7 @@ export interface DrawingProps { changingCmdId?: CommandUid personCursorPos?: Point personCursorVisible?: boolean + enableMiddleMousePanning?: boolean cmdAdded?: (cmd: DrawingCmd) => void cmdChanging?: (id: CommandUid) => void cmdUnchanged?: (id: CommandUid) => void @@ -67,6 +68,7 @@ export interface DrawingProps { personCursorMoved?: (nodePos: Point) => void panning?: (offset: Point) => void panned?: (offset: Point) => void + toolChanged?: (tool: DrawingTool) => void } export type DrawingTool = 'pen' | 'erase' | 'pan' | 'text' @@ -81,6 +83,7 @@ type PointStatus = 'last-point' | 'intermediate-point' class DrawState { on = false + usedBeforePanningTool: DrawingTool | undefined = undefined tool: DrawingTool = 'pen' penColor: ColorMetaNameOrHex = 'alpha' penWidth = 4 @@ -373,6 +376,7 @@ export function drawing ( resizeObserver.observe(canvas) let touchId: number | undefined + let isMiddleMousePanning = false function findTouch (touches: TouchList, id: number | undefined = touchId): Touch | undefined { for (let i = 0; i < touches.length; i++) { @@ -398,7 +402,8 @@ export function drawing ( } const touch = e.changedTouches[0] touchId = touch.identifier - drawStart(touchToNodePoint(touch, canvas)) + const forcePan = false + drawStart(touchToNodePoint(touch, canvas), forcePan) } canvas.ontouchmove = (e) => { @@ -407,7 +412,8 @@ export function drawing ( } const touch = findTouch(e.changedTouches) if (touch !== undefined) { - drawContinue(touchToNodePoint(touch, canvas)) + const forcePan = false + drawContinue(touchToNodePoint(touch, canvas), forcePan) } } @@ -417,7 +423,8 @@ export function drawing ( } const touch = findTouch(e.changedTouches) if (touch !== undefined) { - drawEnd(touchToNodePoint(touch, canvas)) + const forcePan = false + drawEnd(touchToNodePoint(touch, canvas), forcePan) } touchId = undefined } @@ -428,12 +435,22 @@ export function drawing ( if (readonly) { return } + const MiddleMouseButton = 1 + if (e.button === MiddleMouseButton && props.enableMiddleMousePanning === true) { + e.preventDefault() + isMiddleMousePanning = true + canvas.setPointerCapture(e.pointerId) + const forcePan = true + drawStart(pointerToNodePoint(e), forcePan) + return + } if (e.button !== 0) { return } e.preventDefault() canvas.setPointerCapture(e.pointerId) - drawStart(pointerToNodePoint(e)) + const forcePan = false + drawStart(pointerToNodePoint(e), forcePan) } canvas.onpointermove = (e) => { @@ -441,7 +458,8 @@ export function drawing ( return } e.preventDefault() - drawContinue(pointerToNodePoint(e)) + const forcePan = isMiddleMousePanning + drawContinue(pointerToNodePoint(e), forcePan) } canvas.onpointerup = (e) => { @@ -450,10 +468,23 @@ export function drawing ( } e.preventDefault() canvas.releasePointerCapture(e.pointerId) - drawEnd(pointerToNodePoint(e)) + const forcePan = isMiddleMousePanning + drawEnd(pointerToNodePoint(e), forcePan) + if (e.button === 1) { + isMiddleMousePanning = false + } } - canvas.onpointercancel = canvas.onpointerup + canvas.onpointercancel = (e) => { + if (readonly) { + return + } + e.preventDefault() + canvas.releasePointerCapture(e.pointerId) + const forcePan = isMiddleMousePanning + drawEnd(pointerToNodePoint(e), forcePan) + isMiddleMousePanning = false + } canvas.onpointerenter = () => { if (!readonly && draw.isDrawingTool()) { @@ -472,20 +503,20 @@ export function drawing ( return makeMouseScaledPoint(scaled.x, scaled.y) } - function drawStart (p: NodePoint): void { + function drawStart (p: NodePoint, forcePan: boolean): void { const scaledPoint = rescaleWithCss(p) draw.on = true draw.points = [] prevPos = scaledPoint - if (draw.isDrawingTool()) { + if (!forcePan && draw.isDrawingTool()) { draw.addPoint(scaledPoint) } } - function drawContinue (p: NodePoint): void { + function drawContinue (p: NodePoint, forcePan: boolean): void { const scaledPoint = rescaleWithCss(p) - if (draw.isDrawingTool()) { + if (!forcePan && draw.isDrawingTool()) { const cursorSize = draw.cursorWidth() const canvasOffsetInParent = offsetInParent(node, canvas) @@ -501,7 +532,7 @@ export function drawing ( } } - if (draw.on && draw.tool === 'pan') { + if (draw.on && (draw.tool === 'pan' || forcePan)) { requestAnimationFrame(() => { draw.offset.x += scaledPoint.x - prevPos.x draw.offset.y += scaledPoint.y - prevPos.y @@ -513,22 +544,22 @@ export function drawing ( }) } - if (draw.on && draw.tool === 'text') { + if (draw.on && draw.tool === 'text' && !forcePan) { prevPos = scaledPoint } - if (props.pointerMoved !== undefined) { + if (props.pointerMoved !== undefined && !forcePan) { props.pointerMoved(draw.mouseToCanvasPoint(scaledPoint)) } } - function drawEnd (p: NodePoint): void { + function drawEnd (p: NodePoint, forcePan: boolean): void { const scaledPoint = rescaleWithCss(p) if (draw.on) { - if (draw.isDrawingTool()) { + if (!forcePan && draw.isDrawingTool()) { draw.drawLine(scaledPoint, 'last-point', props.getCurrentTheme()) storeLineCommand() - } else if (draw.tool === 'pan') { + } else if (draw.tool === 'pan' || forcePan) { props.panned?.(draw.offset) } else if (draw.tool === 'text') { if (liveTextBox !== undefined) { @@ -1024,6 +1055,11 @@ export function drawing ( } if (props.tool !== undefined) { if (draw.tool !== props.tool) { + if (props.tool === 'pan') { + draw.usedBeforePanningTool = draw.tool + } else { + draw.usedBeforePanningTool = props.tool + } draw.tool = props.tool syncToolCursor = true toolChanged = true @@ -1031,6 +1067,10 @@ export function drawing ( } if (props.penColor !== undefined) { if (draw.penColor !== props.penColor) { + if (draw.tool === 'pan' && draw.usedBeforePanningTool != null) { + draw.tool = draw.usedBeforePanningTool + toolChanged = true + } draw.penColor = props.penColor syncLiveTextBox = true syncToolCursor = true @@ -1084,6 +1124,9 @@ export function drawing ( } } + if (toolChanged) { + props.toolChanged?.(draw.tool) + } if (syncToolCursor) { updateToolCursor() } diff --git a/plugins/text-editor-resources/src/components/DrawingBoardEditor.svelte b/plugins/text-editor-resources/src/components/DrawingBoardEditor.svelte index e2c2cdadb1..331d324fc2 100644 --- a/plugins/text-editor-resources/src/components/DrawingBoardEditor.svelte +++ b/plugins/text-editor-resources/src/components/DrawingBoardEditor.svelte @@ -32,7 +32,6 @@ import { Loading, Component, themeStore } from '@hcengineering/ui' import { onMount, onDestroy } from 'svelte' import { Array as YArray, Map as YMap, Doc as YDoc } from 'yjs' - import { get } from 'svelte/store' export let boardId: string export let document: YDoc @@ -262,6 +261,7 @@ eraserWidth, fontSize, personCursorPos: personCursorCanvasPos, + enableMiddleMousePanning: true, changingCmdId, cmdAdded: (commandToAdd) => { commandsProcessor.addCommand(commandToAdd) @@ -288,6 +288,9 @@ }, personCursorMoved: (nodePos) => { personCursorNodePos = nodePos + }, + toolChanged: (newTool) => { + tool = newTool } }} > From 092f5965fc6cb73b1d889c9b1abc91db118239da Mon Sep 17 00:00:00 2001 From: Alexander Onnikov Date: Tue, 21 Oct 2025 20:49:53 +0700 Subject: [PATCH 3/7] fix: restructure workspace general settings (#10120) Signed-off-by: Alexander Onnikov --- .../src/components/SelectAvatarPopup.svelte | 14 +- plugins/setting-assets/lang/cs.json | 7 +- plugins/setting-assets/lang/de.json | 7 +- plugins/setting-assets/lang/en.json | 7 +- plugins/setting-assets/lang/es.json | 7 +- plugins/setting-assets/lang/fr.json | 7 +- plugins/setting-assets/lang/it.json | 7 +- plugins/setting-assets/lang/ja.json | 7 +- plugins/setting-assets/lang/pt.json | 10 +- plugins/setting-assets/lang/ru.json | 7 +- plugins/setting-assets/lang/tr.json | 7 +- plugins/setting-assets/lang/zh.json | 7 +- .../src/components/ApiTokenPopup.svelte | 104 +++++++++++ .../src/components/Backup.svelte | 2 +- .../src/components/General.svelte | 162 +++++++++++------- .../components/socialIds/SocialIdRow.svelte | 6 - plugins/setting-resources/src/plugin.ts | 5 + 17 files changed, 290 insertions(+), 83 deletions(-) create mode 100644 plugins/setting-resources/src/components/ApiTokenPopup.svelte diff --git a/plugins/contact-resources/src/components/SelectAvatarPopup.svelte b/plugins/contact-resources/src/components/SelectAvatarPopup.svelte index bc9111c28d..021ee19883 100644 --- a/plugins/contact-resources/src/components/SelectAvatarPopup.svelte +++ b/plugins/contact-resources/src/components/SelectAvatarPopup.svelte @@ -19,9 +19,10 @@ import type { Ref } from '@hcengineering/core' import { Blob as PlatformBlob } from '@hcengineering/core' import { Asset } from '@hcengineering/platform' - import presentation, { Card, getFileUrl } from '@hcengineering/presentation' - import { + import presentation, { Card, getFileUrl, uiContext } from '@hcengineering/presentation' + import ui, { AnySvelteComponent, + Button, ColorDefinition, Label, TabList, @@ -171,6 +172,12 @@ } } + function handleClearClick (): void { + selectedAvatar = undefined + selectedAvatarType = AvatarType.COLOR + selectedAvatarProps = { color: getPlatformAvatarColorForTextDef(name ?? '', $themeStore.dark).name } + } + const showColorPopup = (event: MouseEvent) => { showPopup( ColorsPopup, @@ -238,6 +245,9 @@ on:select={handleDropdownSelection} /> + +