mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-06 02:30:47 -08:00
refactor: tidgiminiwindow
This commit is contained in:
parent
5d468b80ca
commit
b376a06b21
42 changed files with 682 additions and 677 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB |
BIN
build-resources/tidgiMiniWindow@2x.png
Normal file
BIN
build-resources/tidgiMiniWindow@2x.png
Normal file
Binary file not shown.
BIN
build-resources/tidgiMiniWindowTemplate@2x.png
Normal file
BIN
build-resources/tidgiMiniWindowTemplate@2x.png
Normal file
Binary file not shown.
|
|
@ -1,37 +0,0 @@
|
|||
@menubar
|
||||
Feature: TidGi Menubar Window
|
||||
As a user
|
||||
I want to enable and use the TidGi menubar window
|
||||
So that I can quickly access TidGi from the system menubar
|
||||
|
||||
Scenario: Enable menubar window and test keyboard shortcut
|
||||
Given I launch the TidGi application
|
||||
And I wait for the page to load completely
|
||||
And I click on an "open preferences button" element with selector "#open-preferences-button"
|
||||
And I switch to "preferences" window
|
||||
When I click on a "menubar section" element with selector "[data-testid='preference-section-menubar']"
|
||||
And I confirm the "menubar" window does not exist
|
||||
When I click on an "attach to menubar switch" element with selector "[data-testid='attach-to-menubar-switch']"
|
||||
And I confirm the "menubar" window exists
|
||||
And I confirm the "menubar" window not visible
|
||||
Then I should see "sidebar toggle and always on top toggle and workspace sync toggle" elements with selectors:
|
||||
| [data-testid='sidebar-on-menubar-switch'] |
|
||||
| [data-testid='menubar-always-on-top-switch'] |
|
||||
| [data-testid='menubar-sync-workspace-switch'] |
|
||||
Then I click on a "shortcut register button" element with selector "[data-testid='shortcut-register-button']"
|
||||
And I press the key combination "CommandOrControl+Shift+M"
|
||||
And I click on a "shortcut confirm button" element with selector "[data-testid='shortcut-confirm-button']"
|
||||
And I close "preferences" window
|
||||
Then I switch to "main" window
|
||||
When I press the key combination "CommandOrControl+Shift+M"
|
||||
And I confirm the "menubar" window exists
|
||||
And I confirm the "menubar" window visible
|
||||
And I confirm the "menubar" window browser view is positioned within visible window bounds
|
||||
And I switch to "menubar" window
|
||||
Then the browser view should be loaded and visible
|
||||
And I should see "我的 TiddlyWiki" in the browser view content
|
||||
Then I switch to "main" window
|
||||
When I press the key combination "CommandOrControl+Shift+M"
|
||||
And I wait for 2 seconds
|
||||
And I confirm the "menubar" window exists
|
||||
And I confirm the "menubar" window not visible
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
@menubar
|
||||
Feature: TidGi Menubar Window Workspace Switching
|
||||
As a user with menubar already enabled
|
||||
I want to test menubar window behavior with different workspace configurations
|
||||
So that I can verify workspace switching and fixed workspace features
|
||||
|
||||
Background:
|
||||
Given I configure menubar with shortcut
|
||||
Then I launch the TidGi application
|
||||
And I wait for the page to load completely
|
||||
Then I switch to "main" window
|
||||
|
||||
Scenario: Menubar window syncs with main window switching to agent workspace
|
||||
# Switch main window to agent workspace
|
||||
When I click on an "agent workspace button" element with selector "[data-testid='workspace-agent']"
|
||||
# Verify menubar window exists in background (created but not visible)
|
||||
And I wait for 0.2 seconds
|
||||
Then I confirm the "menubar" window exists
|
||||
And I confirm the "menubar" window not visible
|
||||
When I press the key combination "CommandOrControl+Shift+M"
|
||||
And I confirm the "menubar" window visible
|
||||
And I confirm the "menubar" window browser view is not positioned within visible window bounds
|
||||
Then I switch to "menubar" window
|
||||
And I should see a "new tab button" element with selector "[data-tab-id='new-tab-button']"
|
||||
|
||||
Scenario: Menubar window with fixed agent workspace shows no view and fixed wiki workspace shows browser view
|
||||
# Configure fixed agent workspace through UI
|
||||
And I click on an "open preferences button" element with selector "#open-preferences-button"
|
||||
And I switch to "preferences" window
|
||||
When I click on a "menubar section" element with selector "[data-testid='preference-section-menubar']"
|
||||
When I click on a "Disable menubar sync workspace switch" element with selector "[data-testid='menubar-sync-workspace-switch']"
|
||||
# Select agent workspace (which is a page type workspace)
|
||||
And I select "agent" from MUI Select with test id "menubar-fixed-workspace-select"
|
||||
And I wait for 0.2 seconds
|
||||
# Open menubar window - should show agent workspace and no browser view
|
||||
When I press the key combination "CommandOrControl+Shift+M"
|
||||
And I confirm the "menubar" window visible
|
||||
And I confirm the "menubar" window browser view is not positioned within visible window bounds
|
||||
Then I switch to "menubar" window
|
||||
And I should see a "new tab button" element with selector "[data-tab-id='new-tab-button']"
|
||||
# Close menubar and switch to wiki workspace
|
||||
And I wait for 0.2 seconds
|
||||
Then I switch to "preferences" window
|
||||
When I press the key combination "CommandOrControl+Shift+M"
|
||||
And I confirm the "menubar" window not visible
|
||||
# Get the first wiki workspace ID and select it
|
||||
And I select "wiki" from MUI Select with test id "menubar-fixed-workspace-select"
|
||||
And I wait for 0.2 seconds
|
||||
# Open menubar window again - should show wiki workspace with browser view
|
||||
When I press the key combination "CommandOrControl+Shift+M"
|
||||
And I confirm the "menubar" window visible
|
||||
And I confirm the "menubar" window browser view is positioned within visible window bounds
|
||||
Then I switch to "menubar" window
|
||||
And the browser view should be loaded and visible
|
||||
And I should see "我的 TiddlyWiki" in the browser view content
|
||||
|
|
@ -8,7 +8,7 @@ import { MockOpenAIServer } from '../supports/mockOpenAI';
|
|||
import { logsDirectory, makeSlugPath, screenshotsDirectory } from '../supports/paths';
|
||||
import { getPackedAppPath } from '../supports/paths';
|
||||
import { clearAISettings } from './agent';
|
||||
import { clearMenubarSettings } from './menuar';
|
||||
import { clearTidgiMiniWindowSettings } from './tidgiMiniWindow';
|
||||
|
||||
export class ApplicationWorld {
|
||||
app: ElectronApplication | undefined;
|
||||
|
|
@ -39,40 +39,40 @@ export class ApplicationWorld {
|
|||
if (mainWindow) return mainWindow;
|
||||
} else if (windowType === 'current') {
|
||||
if (this.currentWindow) return this.currentWindow;
|
||||
} else if (windowType.toLowerCase() === 'menubar') {
|
||||
// Special handling for menubar window
|
||||
// Menubar window is typically the smaller window (500x600)
|
||||
// We identify it by getting all windows and finding the one with menubar URL
|
||||
} else if (windowType.toLowerCase() === 'tidgiminiwindow') {
|
||||
// Special handling for tidgi mini window
|
||||
// TidGi mini window is typically the smaller window (500x600)
|
||||
// We identify it by getting all windows and finding the one with tidgi mini window URL
|
||||
|
||||
const menubarInfo = await this.app.evaluate(async ({ BrowserWindow }) => {
|
||||
const tidgiMiniWindowInfo = await this.app.evaluate(async ({ BrowserWindow }) => {
|
||||
const allWindows = BrowserWindow.getAllWindows();
|
||||
// Find the window with menubar dimensions (500x600)
|
||||
const menubarWindow = allWindows.find(win => {
|
||||
// Find the window with tidgi mini window dimensions (500x600)
|
||||
const tidgiMiniWindow = allWindows.find(win => {
|
||||
const bounds = win.getBounds();
|
||||
return bounds.width === 500 && bounds.height === 600;
|
||||
});
|
||||
|
||||
if (menubarWindow) {
|
||||
if (tidgiMiniWindow) {
|
||||
return {
|
||||
url: menubarWindow.webContents.getURL(),
|
||||
id: menubarWindow.id,
|
||||
url: tidgiMiniWindow.webContents.getURL(),
|
||||
id: tidgiMiniWindow.id,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (menubarInfo) {
|
||||
if (tidgiMiniWindowInfo) {
|
||||
// Wait a bit for the window to be registered in Playwright
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
const refreshedPages = this.app.windows();
|
||||
|
||||
// Find the page with matching URL (exact match)
|
||||
const menubarPage = refreshedPages.find(page => {
|
||||
return page.url() === menubarInfo.url;
|
||||
const tidgiMiniWindowPage = refreshedPages.find(page => {
|
||||
return page.url() === tidgiMiniWindowInfo.url;
|
||||
});
|
||||
|
||||
if (menubarPage) {
|
||||
return menubarPage;
|
||||
if (tidgiMiniWindowPage) {
|
||||
return tidgiMiniWindowPage;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -116,15 +116,15 @@ Before(function(this: ApplicationWorld, { pickle }) {
|
|||
if (pickle.tags.some((tag) => tag.name === '@setup')) {
|
||||
clearAISettings();
|
||||
}
|
||||
if (pickle.tags.some((tag) => tag.name === '@menubar')) {
|
||||
clearMenubarSettings();
|
||||
if (pickle.tags.some((tag) => tag.name === '@tidgiminiwindow')) {
|
||||
clearTidgiMiniWindowSettings();
|
||||
}
|
||||
});
|
||||
|
||||
After(async function(this: ApplicationWorld, { pickle }) {
|
||||
if (this.app) {
|
||||
try {
|
||||
// Close all windows including menubar window before closing the app, otherwise it might hang, and refused to exit until ctrl+C
|
||||
// Close all windows including tidgi mini window before closing the app, otherwise it might hang, and refused to exit until ctrl+C
|
||||
const allWindows = this.app.windows();
|
||||
for (const window of allWindows) {
|
||||
try {
|
||||
|
|
@ -143,8 +143,8 @@ After(async function(this: ApplicationWorld, { pickle }) {
|
|||
this.mainWindow = undefined;
|
||||
this.currentWindow = undefined;
|
||||
}
|
||||
if (pickle.tags.some((tag) => tag.name === '@menubar')) {
|
||||
clearMenubarSettings();
|
||||
if (pickle.tags.some((tag) => tag.name === '@tidgiminiwindow')) {
|
||||
clearTidgiMiniWindowSettings();
|
||||
}
|
||||
if (pickle.tags.some((tag) => tag.name === '@setup')) {
|
||||
clearAISettings();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import path from 'path';
|
|||
import type { ISettingFile } from '../../src/services/database/interface';
|
||||
import { settingsPath } from '../supports/paths';
|
||||
|
||||
Given('I configure menubar with shortcut', async function() {
|
||||
Given('I configure tidgi mini window with shortcut', async function() {
|
||||
let existing = {} as ISettingFile;
|
||||
if (await fs.pathExists(settingsPath)) {
|
||||
existing = await fs.readJson(settingsPath) as ISettingFile;
|
||||
|
|
@ -26,35 +26,35 @@ Given('I configure menubar with shortcut', async function() {
|
|||
|
||||
const updatedPreferences = {
|
||||
...existing.preferences,
|
||||
attachToMenubar: true,
|
||||
attachToTidgiMiniWindow: true,
|
||||
keyboardShortcuts: {
|
||||
...(existing.preferences?.keyboardShortcuts || {}),
|
||||
'Window.toggleMenubarWindow': shortcut,
|
||||
'Window.toggleTidgiMiniWindow': shortcut,
|
||||
},
|
||||
};
|
||||
const finalSettings = { ...existing, preferences: updatedPreferences } as ISettingFile;
|
||||
await fs.writeJson(settingsPath, finalSettings, { spaces: 2 });
|
||||
});
|
||||
|
||||
// Cleanup function to be called after menubar tests (after app closes)
|
||||
function clearMenubarSettings() {
|
||||
// Cleanup function to be called after tidgi mini window tests (after app closes)
|
||||
function clearTidgiMiniWindowSettings() {
|
||||
if (!fs.existsSync(settingsPath)) return;
|
||||
const parsed = fs.readJsonSync(settingsPath) as ISettingFile;
|
||||
// Remove menubar-related preferences to avoid affecting other tests
|
||||
// Remove tidgi mini window-related preferences to avoid affecting other tests
|
||||
const cleanedPreferences = omit(parsed.preferences || {}, [
|
||||
'attachToMenubar',
|
||||
'menubarSyncWorkspaceWithMainWindow',
|
||||
'menubarFixedWorkspaceId',
|
||||
'menuBarAlwaysOnTop',
|
||||
'sidebarOnMenubar',
|
||||
'showMenubarWindowTitleBar',
|
||||
'attachToTidgiMiniWindow',
|
||||
'tidgiMiniWindowSyncWorkspaceWithMainWindow',
|
||||
'tidgiMiniWindowFixedWorkspaceId',
|
||||
'tidgiMiniWindowAlwaysOnTop',
|
||||
'sidebarOnTidgiMiniWindow',
|
||||
'showTidgiMiniWindowTitleBar',
|
||||
]);
|
||||
// Also clean up the menubar shortcut from keyboardShortcuts
|
||||
// Also clean up the tidgi mini window shortcut from keyboardShortcuts
|
||||
if (cleanedPreferences.keyboardShortcuts) {
|
||||
cleanedPreferences.keyboardShortcuts = omit(cleanedPreferences.keyboardShortcuts, ['Window.toggleMenubarWindow']);
|
||||
cleanedPreferences.keyboardShortcuts = omit(cleanedPreferences.keyboardShortcuts, ['Window.toggleTidgiMiniWindow']);
|
||||
}
|
||||
const cleaned = { ...parsed, preferences: cleanedPreferences };
|
||||
fs.writeJsonSync(settingsPath, cleaned, { spaces: 2 });
|
||||
}
|
||||
|
||||
export { clearMenubarSettings };
|
||||
export { clearTidgiMiniWindowSettings };
|
||||
|
|
@ -11,23 +11,23 @@ const DEFAULT_RETRY_INTERVAL_MS = 250;
|
|||
async function findWindowByType(app: ElectronApplication, windowType: string): Promise<Page | undefined> {
|
||||
const pages = app.windows();
|
||||
|
||||
if (windowType.toLowerCase() === 'menubar') {
|
||||
// Special handling for menubar window
|
||||
// Menubar window uses menubar library and may not have a standard route
|
||||
if (windowType.toLowerCase() === 'tidgiminiwindow') {
|
||||
// Special handling for tidgi mini window
|
||||
// TidGi mini window may not have a standard route
|
||||
// Look for window by its title or other properties
|
||||
const allWindows = pages.filter(page => !page.isClosed());
|
||||
// Try to find by URL pattern first
|
||||
let menubarWindow = allWindows.find(page => {
|
||||
let tidgiMiniWindow = allWindows.find(page => {
|
||||
const url = page.url() || '';
|
||||
return url.includes('#/menuBar') || url.includes('#/main');
|
||||
return url.includes('#/tidgiMiniWindow') || url.includes('#/main');
|
||||
});
|
||||
// If not found by URL, try by window properties
|
||||
if (!menubarWindow && allWindows.length > 1) {
|
||||
// Menubar is typically a smaller window
|
||||
if (!tidgiMiniWindow && allWindows.length > 1) {
|
||||
// TidGi mini window is typically a smaller window
|
||||
// Get the second window (first is main)
|
||||
menubarWindow = allWindows[1];
|
||||
tidgiMiniWindow = allWindows[1];
|
||||
}
|
||||
return menubarWindow;
|
||||
return tidgiMiniWindow;
|
||||
} else {
|
||||
// For regular windows (preferences, about, addWorkspace, etc.)
|
||||
return pages.find(page => {
|
||||
|
|
@ -76,27 +76,27 @@ When('I confirm the {string} window exists', async function(this: ApplicationWor
|
|||
throw new Error('Application is not launched');
|
||||
}
|
||||
|
||||
if (windowType.toLowerCase() === 'menubar') {
|
||||
// For menubar, check via Electron API since it's a special tray window
|
||||
// Menubar is created by the menubar library and may take time to initialize
|
||||
if (windowType.toLowerCase() === 'tidgiminiwindow') {
|
||||
// For tidgi mini window, check via Electron API since it's a special tray window
|
||||
// TidGi mini window may take time to initialize
|
||||
const maxWaitAttempts = 10; // Wait up to 10 seconds
|
||||
const waitInterval = 1000; // Check every second
|
||||
|
||||
for (let attempt = 0; attempt < maxWaitAttempts; attempt++) {
|
||||
const windowInfo = await this.app.evaluate(async ({ BrowserWindow }) => {
|
||||
const allWindows = BrowserWindow.getAllWindows();
|
||||
// Look for menubar window specifically by its dimensions (500x600)
|
||||
const menubarWindow = allWindows.find(win => {
|
||||
// Look for tidgi mini window specifically by its dimensions (500x600)
|
||||
const tidgiMiniWindow = allWindows.find(win => {
|
||||
const bounds = win.getBounds();
|
||||
return bounds.width === 500 && bounds.height === 600;
|
||||
});
|
||||
|
||||
return {
|
||||
hasMenubar: menubarWindow !== undefined,
|
||||
hasTidgiMiniWindow: tidgiMiniWindow !== undefined,
|
||||
};
|
||||
});
|
||||
|
||||
if (windowInfo.hasMenubar) {
|
||||
if (windowInfo.hasTidgiMiniWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -123,27 +123,27 @@ When('I confirm the {string} window visible', async function(this: ApplicationWo
|
|||
throw new Error('Application is not launched');
|
||||
}
|
||||
|
||||
if (windowType.toLowerCase() === 'menubar') {
|
||||
// Special handling for menubar window visibility
|
||||
// Menubar window may take longer to show after shortcut key
|
||||
const menubarMaxAttempts = 10; // Wait up to 10 seconds
|
||||
const menubarWaitInterval = 1000; // Check every second
|
||||
if (windowType.toLowerCase() === 'tidgiminiwindow') {
|
||||
// Special handling for tidgi mini window visibility
|
||||
// TidGi mini window may take longer to show after shortcut key
|
||||
const tidgiMiniWindowMaxAttempts = 10; // Wait up to 10 seconds
|
||||
const tidgiMiniWindowWaitInterval = 1000; // Check every second
|
||||
|
||||
for (let attempt = 0; attempt < menubarMaxAttempts; attempt++) {
|
||||
for (let attempt = 0; attempt < tidgiMiniWindowMaxAttempts; attempt++) {
|
||||
const windowInfo = await this.app.evaluate(async ({ BrowserWindow }) => {
|
||||
const allWindows = BrowserWindow.getAllWindows();
|
||||
const menubarWindow = allWindows.find(win => {
|
||||
const tidgiMiniWindow = allWindows.find(win => {
|
||||
const bounds = win.getBounds();
|
||||
return bounds.width === 500 && bounds.height === 600;
|
||||
});
|
||||
|
||||
if (!menubarWindow) {
|
||||
if (!tidgiMiniWindow) {
|
||||
return { found: false, visible: false };
|
||||
}
|
||||
|
||||
return {
|
||||
found: true,
|
||||
visible: menubarWindow.isVisible(),
|
||||
visible: tidgiMiniWindow.isVisible(),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -151,9 +151,9 @@ When('I confirm the {string} window visible', async function(this: ApplicationWo
|
|||
return;
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, menubarWaitInterval));
|
||||
await new Promise(resolve => setTimeout(resolve, tidgiMiniWindowWaitInterval));
|
||||
}
|
||||
throw new Error(`${windowType} window was not visible after ${menubarMaxAttempts} attempts`);
|
||||
throw new Error(`${windowType} window was not visible after ${tidgiMiniWindowMaxAttempts} attempts`);
|
||||
}
|
||||
|
||||
const success = await waitForWindowCondition(
|
||||
|
|
@ -174,23 +174,23 @@ When('I confirm the {string} window not visible', async function(this: Applicati
|
|||
throw new Error('Application is not launched');
|
||||
}
|
||||
|
||||
if (windowType.toLowerCase() === 'menubar') {
|
||||
// Special handling for menubar window visibility check
|
||||
if (windowType.toLowerCase() === 'tidgiminiwindow') {
|
||||
// Special handling for tidgi mini window visibility check
|
||||
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
||||
const windowInfo = await this.app.evaluate(async ({ BrowserWindow }) => {
|
||||
const allWindows = BrowserWindow.getAllWindows();
|
||||
const menubarWindow = allWindows.find(win => {
|
||||
const tidgiMiniWindow = allWindows.find(win => {
|
||||
const bounds = win.getBounds();
|
||||
return bounds.width === 500 && bounds.height === 600;
|
||||
});
|
||||
|
||||
if (!menubarWindow) {
|
||||
if (!tidgiMiniWindow) {
|
||||
return { found: false, visible: false };
|
||||
}
|
||||
|
||||
return {
|
||||
found: true,
|
||||
visible: menubarWindow.isVisible(),
|
||||
visible: tidgiMiniWindow.isVisible(),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -219,23 +219,23 @@ When('I confirm the {string} window does not exist', async function(this: Applic
|
|||
throw new Error('Application is not launched');
|
||||
}
|
||||
|
||||
if (windowType.toLowerCase() === 'menubar') {
|
||||
// Special handling for menubar window
|
||||
if (windowType.toLowerCase() === 'tidgiminiwindow') {
|
||||
// Special handling for tidgi mini window
|
||||
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
||||
const windowInfo = await this.app.evaluate(async ({ BrowserWindow }) => {
|
||||
const allWindows = BrowserWindow.getAllWindows();
|
||||
// Look for menubar window specifically by its dimensions (500x600)
|
||||
const menubarWindow = allWindows.find(win => {
|
||||
// Look for tidgi mini window specifically by its dimensions (500x600)
|
||||
const tidgiMiniWindow = allWindows.find(win => {
|
||||
const bounds = win.getBounds();
|
||||
return bounds.width === 500 && bounds.height === 600;
|
||||
});
|
||||
|
||||
return {
|
||||
hasMenubar: menubarWindow !== undefined,
|
||||
hasTidgiMiniWindow: tidgiMiniWindow !== undefined,
|
||||
};
|
||||
});
|
||||
|
||||
if (!windowInfo.hasMenubar) {
|
||||
if (!windowInfo.hasTidgiMiniWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +268,7 @@ When('I confirm the {string} window browser view is positioned within visible wi
|
|||
}
|
||||
|
||||
// Get the window dimensions to identify it
|
||||
const windowDimensions = windowType.toLowerCase() === 'menubar'
|
||||
const windowDimensions = windowType.toLowerCase() === 'tidgiminiwindow'
|
||||
? { width: 500, height: 600 }
|
||||
: { width: 1178, height: 686 }; // main window default
|
||||
|
||||
|
|
@ -349,7 +349,7 @@ When('I confirm the {string} window browser view is not positioned within visibl
|
|||
}
|
||||
|
||||
// Get the window dimensions to identify it
|
||||
const windowDimensions = windowType.toLowerCase() === 'menubar'
|
||||
const windowDimensions = windowType.toLowerCase() === 'tidgiminiwindow'
|
||||
? { width: 500, height: 600 }
|
||||
: { width: 1178, height: 686 }; // main window default
|
||||
|
||||
|
|
|
|||
37
features/tidgiMiniWindow.feature
Normal file
37
features/tidgiMiniWindow.feature
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
@tidgiminiwindow
|
||||
Feature: TidGi Mini Window
|
||||
As a user
|
||||
I want to enable and use the TidGi mini window
|
||||
So that I can quickly access TidGi from the system tray
|
||||
|
||||
Scenario: Enable tidgi mini window and test keyboard shortcut
|
||||
Given I launch the TidGi application
|
||||
And I wait for the page to load completely
|
||||
And I click on an "open preferences button" element with selector "#open-preferences-button"
|
||||
And I switch to "preferences" window
|
||||
When I click on a "tidgi mini window section" element with selector "[data-testid='preference-section-tidgiMiniWindow']"
|
||||
And I confirm the "tidgiminiwindow" window does not exist
|
||||
When I click on an "attach to tidgi mini window switch" element with selector "[data-testid='attach-to-tidgi-mini-window-switch']"
|
||||
And I confirm the "tidgiminiwindow" window exists
|
||||
And I confirm the "tidgiminiwindow" window not visible
|
||||
Then I should see "sidebar toggle and always on top toggle and workspace sync toggle" elements with selectors:
|
||||
| [data-testid='sidebar-on-tidgi-mini-window-switch'] |
|
||||
| [data-testid='tidgi-mini-window-always-on-top-switch'] |
|
||||
| [data-testid='tidgi-mini-window-sync-workspace-switch'] |
|
||||
Then I click on a "shortcut register button" element with selector "[data-testid='shortcut-register-button']"
|
||||
And I press the key combination "CommandOrControl+Shift+M"
|
||||
And I click on a "shortcut confirm button" element with selector "[data-testid='shortcut-confirm-button']"
|
||||
And I close "preferences" window
|
||||
Then I switch to "main" window
|
||||
When I press the key combination "CommandOrControl+Shift+M"
|
||||
And I confirm the "tidgiminiwindow" window exists
|
||||
And I confirm the "tidgiminiwindow" window visible
|
||||
And I confirm the "tidgiminiwindow" window browser view is positioned within visible window bounds
|
||||
And I switch to "tidgiminiwindow" window
|
||||
Then the browser view should be loaded and visible
|
||||
And I should see "我的 TiddlyWiki" in the browser view content
|
||||
Then I switch to "main" window
|
||||
When I press the key combination "CommandOrControl+Shift+M"
|
||||
And I wait for 2 seconds
|
||||
And I confirm the "tidgiminiwindow" window exists
|
||||
And I confirm the "tidgiminiwindow" window not visible
|
||||
55
features/tidgiMiniWindowWorkspace.feature
Normal file
55
features/tidgiMiniWindowWorkspace.feature
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
@tidgiminiwindow
|
||||
Feature: TidGi Mini Window Workspace Switching
|
||||
As a user with tidgi mini window already enabled
|
||||
I want to test tidgi mini window behavior with different workspace configurations
|
||||
So that I can verify workspace switching and fixed workspace features
|
||||
|
||||
Background:
|
||||
Given I configure tidgi mini window with shortcut
|
||||
Then I launch the TidGi application
|
||||
And I wait for the page to load completely
|
||||
Then I switch to "main" window
|
||||
|
||||
Scenario: TidGi mini window syncs with main window switching to agent workspace
|
||||
# Switch main window to agent workspace
|
||||
When I click on an "agent workspace button" element with selector "[data-testid='workspace-agent']"
|
||||
# Verify tidgi mini window exists in background (created but not visible)
|
||||
And I wait for 0.2 seconds
|
||||
Then I confirm the "tidgiminiwindow" window exists
|
||||
And I confirm the "tidgiminiwindow" window not visible
|
||||
When I press the key combination "CommandOrControl+Shift+M"
|
||||
And I confirm the "tidgiminiwindow" window visible
|
||||
And I confirm the "tidgiminiwindow" window browser view is not positioned within visible window bounds
|
||||
Then I switch to "tidgiminiwindow" window
|
||||
And I should see a "new tab button" element with selector "[data-tab-id='new-tab-button']"
|
||||
|
||||
Scenario: TidGi mini window with fixed agent workspace shows no view and fixed wiki workspace shows browser view
|
||||
# Configure fixed agent workspace through UI
|
||||
And I click on an "open preferences button" element with selector "#open-preferences-button"
|
||||
And I switch to "preferences" window
|
||||
When I click on a "tidgi mini window section" element with selector "[data-testid='preference-section-tidgiMiniWindow']"
|
||||
When I click on a "Disable tidgi mini window sync workspace switch" element with selector "[data-testid='tidgi-mini-window-sync-workspace-switch']"
|
||||
# Select agent workspace (which is a page type workspace)
|
||||
And I select "agent" from MUI Select with test id "tidgi-mini-window-fixed-workspace-select"
|
||||
And I wait for 0.2 seconds
|
||||
# Open tidgi mini window - should show agent workspace and no browser view
|
||||
When I press the key combination "CommandOrControl+Shift+M"
|
||||
And I confirm the "tidgiminiwindow" window visible
|
||||
And I confirm the "tidgiminiwindow" window browser view is not positioned within visible window bounds
|
||||
Then I switch to "tidgiminiwindow" window
|
||||
And I should see a "new tab button" element with selector "[data-tab-id='new-tab-button']"
|
||||
# Close tidgi mini window and switch to wiki workspace
|
||||
And I wait for 0.2 seconds
|
||||
Then I switch to "preferences" window
|
||||
When I press the key combination "CommandOrControl+Shift+M"
|
||||
And I confirm the "tidgiminiwindow" window not visible
|
||||
# Get the first wiki workspace ID and select it
|
||||
And I select "wiki" from MUI Select with test id "tidgi-mini-window-fixed-workspace-select"
|
||||
And I wait for 0.2 seconds
|
||||
# Open tidgi mini window again - should show wiki workspace with browser view
|
||||
When I press the key combination "CommandOrControl+Shift+M"
|
||||
And I confirm the "tidgiminiwindow" window visible
|
||||
And I confirm the "tidgiminiwindow" window browser view is positioned within visible window bounds
|
||||
Then I switch to "tidgiminiwindow" window
|
||||
And the browser view should be loaded and visible
|
||||
And I should see "我的 TiddlyWiki" in the browser view content
|
||||
|
|
@ -32,7 +32,7 @@ const config: ForgeConfig = {
|
|||
// Unpack worker files, native modules path, and ALL .node binaries (including better-sqlite3)
|
||||
unpack: '{**/.webpack/main/*.worker.*,**/.webpack/main/native_modules/path.txt,**/{.**,**}/**/*.node}',
|
||||
},
|
||||
extraResource: ['localization', 'template/wiki', 'build-resources/menubar@2x.png', 'build-resources/menubarTemplate@2x.png'],
|
||||
extraResource: ['localization', 'template/wiki', 'build-resources/tidgiMiniWindow@2x.png', 'build-resources/tidgiMiniWindowTemplate@2x.png'],
|
||||
// @ts-expect-error - mac config is valid
|
||||
mac: {
|
||||
category: 'productivity',
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@
|
|||
"OpenCommandPalette": "Open CommandPalette",
|
||||
"OpenLinkInBrowser": "Open Link in Browser",
|
||||
"OpenTidGi": "Open TidGi",
|
||||
"OpenTidGiMenuBar": "Open TidGi MenuBar",
|
||||
"OpenTidGiMiniWindow": "Open TidGi Mini Window",
|
||||
"OpenWorkspaceInNewWindow": "Open Workspace in New Window",
|
||||
"Paste": "Paste",
|
||||
"Preferences": "Preferences...",
|
||||
|
|
@ -309,7 +309,7 @@
|
|||
"SelectNextWorkspace": "Select Next Workspace",
|
||||
"SelectPreviousWorkspace": "Select Previous Workspace",
|
||||
"TidGi": "TidGi",
|
||||
"TidGiMenuBar": "TidGi MenuBar",
|
||||
"TidGiMiniWindow": "TidGi Mini Window",
|
||||
"View": "View",
|
||||
"Wiki": "Wiki",
|
||||
"Window": "Window",
|
||||
|
|
@ -324,10 +324,10 @@
|
|||
"AlwaysOnTopDetail": "Keep TidGi’s main window always on top of other windows, and will not be covered by other windows",
|
||||
"AntiAntiLeech": "Some website has Anti-Leech, will prevent some images from being displayed on your wiki, we simulate a request header that looks like visiting that website to bypass this protection.",
|
||||
"AskDownloadLocation": "Ask where to save each file before downloading",
|
||||
"AttachToMenuBar": "Attach to menu bar",
|
||||
"AttachToMenuBarShowSidebar": "Attach To Menu Bar Show Sidebar",
|
||||
"AttachToMenuBarShowSidebarTip": "Generally, TidGi small window is only used to quickly view the current workspace, so the default synchronization with the main window workspace, do not need a sidebar, the default hidden sidebar.",
|
||||
"AttachToMenuBarTip": "Make a small TidGi popup window that pop when you click appbar mini icon. Tip: Right-click on mini app icon to access context menu.",
|
||||
"AttachToTidgiMiniWindow": "Attach to TidGi mini window",
|
||||
"AttachToTidgiMiniWindowShowSidebar": "Attach To TidGi Mini Window Show Sidebar",
|
||||
"AttachToTidgiMiniWindowShowSidebarTip": "Generally, TidGi mini window is only used to quickly view the current workspace, so the default synchronization with the main window workspace, do not need a sidebar, the default hidden sidebar.",
|
||||
"AttachToTidgiMiniWindowTip": "Make a small TidGi popup window that pop when you click system tray mini icon. Tip: Right-click on mini app icon to access context menu.",
|
||||
"AttachToTaskbar": "Attach to taskbar",
|
||||
"AttachToTaskbarShowSidebar": "Attach To Taskbar Show Sidebar",
|
||||
"ChooseLanguage": "Choose Language 选择语言",
|
||||
|
|
@ -362,16 +362,16 @@
|
|||
"ItIsWorking": "It is working!",
|
||||
"Languages": "Lang/语言",
|
||||
"LightTheme": "Light Theme",
|
||||
"MenubarAlwaysOnTop": "Menubar Always on top",
|
||||
"MenubarAlwaysOnTopDetail": "Keep TidGi's Menubar always on top of other windows, and will not be covered by other windows",
|
||||
"MenubarFixedWorkspace": "Select workspace for fixed menubar window",
|
||||
"MenubarShortcutKey": "Set shortcut key to toggle TidGi menubar window",
|
||||
"MenubarShortcutKeyHelperText": "Set a shortcut key to quickly open or close TidGi menubar window",
|
||||
"MenubarShortcutKeyPlaceholder": "e.g.: Ctrl+Shift+D",
|
||||
"MenubarSyncWorkspaceWithMainWindow": "Menubar window syncs with main window workspace",
|
||||
"MenubarSyncWorkspaceWithMainWindowDetail": "When checked, menubar window will display the same workspace content as main window",
|
||||
"ShowMenubarWindowTitleBar": "Show title bar on menubar window",
|
||||
"ShowMenubarWindowTitleBarDetail": "Show draggable title bar on menubar window",
|
||||
"TidgiMiniWindowAlwaysOnTop": "TidGi Mini Window Always on top",
|
||||
"TidgiMiniWindowAlwaysOnTopDetail": "Keep TidGi's Mini Window always on top of other windows, and will not be covered by other windows",
|
||||
"TidgiMiniWindowFixedWorkspace": "Select workspace for fixed TidGi Mini Window",
|
||||
"TidgiMiniWindowShortcutKey": "Set shortcut key to toggle TidGi Mini Window",
|
||||
"TidgiMiniWindowShortcutKeyHelperText": "Set a shortcut key to quickly open or close TidGi Mini Window",
|
||||
"TidgiMiniWindowShortcutKeyPlaceholder": "e.g.: Ctrl+Shift+D",
|
||||
"TidgiMiniWindowSyncWorkspaceWithMainWindow": "TidGi Mini Window syncs with main window workspace",
|
||||
"TidgiMiniWindowSyncWorkspaceWithMainWindowDetail": "When checked, TidGi Mini Window will display the same workspace content as main window",
|
||||
"ShowTidgiMiniWindowTitleBar": "Show title bar on TidGi Mini Window",
|
||||
"ShowTidgiMiniWindowTitleBarDetail": "Show draggable title bar on TidGi Mini Window",
|
||||
"Miscellaneous": "Miscellaneous",
|
||||
"MoreWorkspaceSyncSettings": "More Workspace Sync Settings",
|
||||
"MoreWorkspaceSyncSettingsDescription": "Please right-click the workspace icon, open its workspace setting by click on \"Edit Workspace\" context menu item, and configure its independent synchronization settings in it.",
|
||||
|
|
@ -438,7 +438,7 @@
|
|||
"TestNotificationDescription": "<0>If notifications dont show up, make sure you enable notifications in<1>macOS Preferences → Notifications → TidGi</1>.</0>",
|
||||
"Theme": "Theme",
|
||||
"TiddlyWiki": "TiddlyWiki",
|
||||
"ToggleMenuBar": "Toggle Menu Bar",
|
||||
"ToggleTidgiMiniWindow": "Toggle TidGi Mini Window",
|
||||
"Token": "Git credentials",
|
||||
"TokenDescription": "The credentials used to authenticate to the Git server so you can securely synchronize content. Can be obtained by logging in to storage services (e.g., Github), or manually obtain \"personal access token\" and filled in here.",
|
||||
"Translatium": "Translatium",
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@
|
|||
"OpenCommandPalette": "Ouvrir la palette de commandes",
|
||||
"OpenLinkInBrowser": "Ouvrir le lien dans le navigateur",
|
||||
"OpenTidGi": "Ouvrir TidGi",
|
||||
"OpenTidGiMenuBar": "Ouvrir la barre de menu TidGi",
|
||||
"OpenTidGiMiniWindow": "Ouvrir la mini-fenêtre TidGi",
|
||||
"OpenWorkspaceInNewWindow": "Ouvrir l'espace de travail dans une nouvelle fenêtre",
|
||||
"Paste": "Coller",
|
||||
"Preferences": "Préférences...",
|
||||
|
|
@ -309,7 +309,7 @@
|
|||
"SelectNextWorkspace": "Sélectionner l'espace de travail suivant",
|
||||
"SelectPreviousWorkspace": "Sélectionner l'espace de travail précédent",
|
||||
"TidGi": "TidGi",
|
||||
"TidGiMenuBar": "Barre de menu TidGi",
|
||||
"TidGiMiniWindow": "Mini-fenêtre TidGi",
|
||||
"View": "Vue",
|
||||
"Wiki": "Wiki",
|
||||
"Window": "Fenêtre",
|
||||
|
|
@ -324,10 +324,10 @@
|
|||
"AlwaysOnTopDetail": "Garder la fenêtre principale de TidGi toujours au-dessus des autres fenêtres, et ne sera pas couverte par d'autres fenêtres",
|
||||
"AntiAntiLeech": "Certains sites web ont une protection anti-leech, empêchant certaines images d'être affichées sur votre wiki, nous simulons un en-tête de requête qui ressemble à la visite de ce site web pour contourner cette protection.",
|
||||
"AskDownloadLocation": "Demander où enregistrer chaque fichier avant de télécharger",
|
||||
"AttachToMenuBar": "Attacher à la barre de menu",
|
||||
"AttachToMenuBarShowSidebar": "Attacher à la barre de menu Afficher la barre latérale",
|
||||
"AttachToMenuBarShowSidebarTip": "En général, la petite fenêtre TidGi est uniquement utilisée pour visualiser rapidement l'espace de travail actuel, donc la synchronisation avec l'espace de travail de la fenêtre principale n'est pas nécessaire, la barre latérale est masquée par défaut.",
|
||||
"AttachToMenuBarTip": "Créer une petite fenêtre contextuelle TidGi qui apparaît lorsque vous cliquez sur l'icône mini de la barre d'application. Astuce : Cliquez avec le bouton droit sur l'icône mini de l'application pour accéder au menu contextuel.",
|
||||
"AttachToTidgiMiniWindow": "Attacher à la mini-fenêtre TidGi",
|
||||
"AttachToTidgiMiniWindowShowSidebar": "Attacher à la mini-fenêtre TidGi Afficher la barre latérale",
|
||||
"AttachToTidgiMiniWindowShowSidebarTip": "En général, la petite fenêtre TidGi est uniquement utilisée pour visualiser rapidement l'espace de travail actuel, donc la synchronisation avec l'espace de travail de la fenêtre principale n'est pas nécessaire, la barre latérale est masquée par défaut.",
|
||||
"AttachToTidgiMiniWindowTip": "Créer une petite fenêtre contextuelle TidGi qui apparaît lorsque vous cliquez sur l'icône mini de la barre d'application. Astuce : Cliquez avec le bouton droit sur l'icône mini de l'application pour accéder au menu contextuel.",
|
||||
"AttachToTaskbar": "Attacher à la barre des tâches",
|
||||
"AttachToTaskbarShowSidebar": "Attacher à la barre des tâches Afficher la barre latérale",
|
||||
"ChooseLanguage": "Choisir la langue 选择语言",
|
||||
|
|
@ -349,8 +349,8 @@
|
|||
"General": "UI & Interact",
|
||||
"HibernateAllUnusedWorkspaces": "Mettre en veille les espaces de travail inutilisés au lancement de l'application",
|
||||
"HibernateAllUnusedWorkspacesDescription": "Mettre en veille tous les espaces de travail au lancement, sauf le dernier espace de travail actif.",
|
||||
"HideMenuBar": "Masquer la barre de menu",
|
||||
"HideMenuBarDetail": "Masquer la barre de menu sauf si Alt+M est pressé.",
|
||||
"HideTidgiMiniWindow": "Masquer la mini-fenêtre TidGi",
|
||||
"HideTidgiMiniWindowDetail": "Masquer la mini-fenêtre TidGi sauf si Alt+M est pressé.",
|
||||
"HideSideBar": "Masquer la barre latérale",
|
||||
"HideSideBarIconDetail": "Masquer l'icône et n'afficher que le nom de l'espace de travail pour rendre la liste des espaces de travail plus compacte",
|
||||
"HideTitleBar": "Masquer la barre de titre",
|
||||
|
|
@ -360,8 +360,8 @@
|
|||
"ItIsWorking": "Ça fonctionne !",
|
||||
"Languages": "Lang/语言",
|
||||
"LightTheme": "Thème clair",
|
||||
"MenubarAlwaysOnTop": "Barre de menu toujours au-dessus",
|
||||
"MenubarAlwaysOnTopDetail": "Garder la barre de menu de TidGi toujours au-dessus des autres fenêtres, et ne sera pas couverte par d'autres fenêtres",
|
||||
"TidgiMiniWindowAlwaysOnTop": "TidGi Mini Window toujours au-dessus",
|
||||
"TidgiMiniWindowAlwaysOnTopDetail": "Garder la Mini Window de TidGi toujours au-dessus des autres fenêtres, et ne sera pas couverte par d'autres fenêtres",
|
||||
"Miscellaneous": "Divers",
|
||||
"MoreWorkspaceSyncSettings": "Plus de paramètres de synchronisation de l'espace de travail",
|
||||
"MoreWorkspaceSyncSettingsDescription": "Veuillez cliquer avec le bouton droit sur l'icône de l'espace de travail, ouvrir ses paramètres d'espace de travail en cliquant sur l'élément de menu contextuel \"Modifier l'espace de travail\", et configurer ses paramètres de synchronisation indépendants.",
|
||||
|
|
@ -414,7 +414,7 @@
|
|||
"TestNotificationDescription": "<0>Si les notifications ne s'affichent pas, assurez-vous d'activer les notifications dans<1>Préférences macOS → Notifications → TidGi</1>.</0>",
|
||||
"Theme": "Thème",
|
||||
"TiddlyWiki": "TiddlyWiki",
|
||||
"ToggleMenuBar": "Basculer la barre de menu",
|
||||
"ToggleTidgiMiniWindow": "Basculer la mini-fenêtre TidGi",
|
||||
"Token": "Informations d'identification Git",
|
||||
"TokenDescription": "Les informations d'identification utilisées pour s'authentifier auprès du serveur Git afin de pouvoir synchroniser le contenu en toute sécurité. Peut être obtenu en se connectant à des services de stockage (par exemple, Github), ou en obtenant manuellement un \"jeton d'accès personnel\" et en le remplissant ici.",
|
||||
"Translatium": "Translatium",
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@
|
|||
"OpenCommandPalette": "コマンドパレットを開く",
|
||||
"OpenLinkInBrowser": "ブラウザでリンクを開く",
|
||||
"OpenTidGi": "TidGiを開く",
|
||||
"OpenTidGiMenuBar": "TidGiメニューバーを開く",
|
||||
"OpenTidGiMiniWindow": "TidGiミニウィンドウを開く",
|
||||
"OpenWorkspaceInNewWindow": "ワークスペースを新しいウィンドウで開く",
|
||||
"Paste": "貼り付け",
|
||||
"Preferences": "設定...",
|
||||
|
|
@ -359,8 +359,8 @@
|
|||
"ItIsWorking": "使いやすい!",
|
||||
"Languages": "言語/ランゲージ",
|
||||
"LightTheme": "明るい色のテーマ",
|
||||
"MenubarAlwaysOnTop": "メニューバーの小ウィンドウを他のウィンドウの上に保持する",
|
||||
"MenubarAlwaysOnTopDetail": "太記のメニューバーウィンドウを常に他のウィンドウの上に表示させ、他のウィンドウで覆われないようにします。",
|
||||
"TidgiMiniWindowAlwaysOnTop": "太記小ウィンドウを他のウィンドウの上に保持する",
|
||||
"TidgiMiniWindowAlwaysOnTopDetail": "太記の小ウィンドウを常に他のウィンドウの上に表示させ、他のウィンドウで覆われないようにします。",
|
||||
"Miscellaneous": "その他の設定",
|
||||
"MoreWorkspaceSyncSettings": "さらに多くのワークスペース同期設定",
|
||||
"MoreWorkspaceSyncSettingsDescription": "ワークスペースアイコンを右クリックし、右クリックメニューから「ワークスペースの編集」を選択して、ワークスペース設定を開いてください。そこで各ワークスペースの同期設定を行います。",
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@
|
|||
"OpenCommandPalette": "Открыть палитру команд",
|
||||
"OpenLinkInBrowser": "Открыть ссылку в браузере",
|
||||
"OpenTidGi": "Открыть TidGi",
|
||||
"OpenTidGiMenuBar": "Открыть меню TidGi",
|
||||
"OpenTidGiMiniWindow": "Открыть мини-окно TidGi",
|
||||
"OpenWorkspaceInNewWindow": "Открыть рабочее пространство в новом окне",
|
||||
"Paste": "Вставить",
|
||||
"Preferences": "Настройки...",
|
||||
|
|
@ -309,7 +309,7 @@
|
|||
"SelectNextWorkspace": "Выбрать следующее рабочее пространство",
|
||||
"SelectPreviousWorkspace": "Выбрать предыдущее рабочее пространство",
|
||||
"TidGi": "TidGi",
|
||||
"TidGiMenuBar": "Слишком помню маленькое окно.",
|
||||
"TidGiMiniWindow": "Мини-окно TidGi",
|
||||
"View": "Просмотр",
|
||||
"Wiki": "Wiki",
|
||||
"Window": "Окно",
|
||||
|
|
@ -359,8 +359,8 @@
|
|||
"ItIsWorking": "Работает!",
|
||||
"Languages": "Языки",
|
||||
"LightTheme": "Светлая тема",
|
||||
"MenubarAlwaysOnTop": "Меню всегда сверху",
|
||||
"MenubarAlwaysOnTopDetail": "Детали меню всегда сверху",
|
||||
"TidgiMiniWindowAlwaysOnTop": "TidGi мини-окно всегда сверху",
|
||||
"TidgiMiniWindowAlwaysOnTopDetail": "Держать мини-окно TidGi всегда поверх других окон",
|
||||
"Miscellaneous": "Разное",
|
||||
"MoreWorkspaceSyncSettings": "Дополнительные настройки синхронизации рабочего пространства",
|
||||
"MoreWorkspaceSyncSettingsDescription": "Описание дополнительных настроек синхронизации рабочего пространства",
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@
|
|||
"OpenCommandPalette": "打开搜索与命令面板",
|
||||
"OpenLinkInBrowser": "在浏览器中打开链接",
|
||||
"OpenTidGi": "打开太记",
|
||||
"OpenTidGiMenuBar": "打开太记小窗口",
|
||||
"OpenTidGiMiniWindow": "打开太记小窗口",
|
||||
"OpenWorkspaceInNewWindow": "在新窗口中打开工作区",
|
||||
"Paste": "粘贴",
|
||||
"Preferences": "设置...",
|
||||
|
|
@ -317,7 +317,7 @@
|
|||
"SelectNextWorkspace": "选择下一个工作区",
|
||||
"SelectPreviousWorkspace": "选择前一个工作区",
|
||||
"TidGi": "太记",
|
||||
"TidGiMenuBar": "太记小窗",
|
||||
"TidGiMiniWindow": "太记小窗",
|
||||
"View": "查看",
|
||||
"Wiki": "知识库",
|
||||
"Window": "窗口",
|
||||
|
|
@ -332,10 +332,10 @@
|
|||
"AlwaysOnTopDetail": "让太记的主窗口永远保持在其它窗口上方,不会被其他窗口覆盖",
|
||||
"AntiAntiLeech": "有的网站做了防盗链,会阻止某些图片在你的知识库上显示,我们通过模拟访问该网站的请求头来绕过这种限制。",
|
||||
"AskDownloadLocation": "下载前询问每个文件的保存位置",
|
||||
"AttachToMenuBar": "附加到菜单栏",
|
||||
"AttachToMenuBarShowSidebar": "附加到菜单栏的窗口包含侧边栏",
|
||||
"AttachToMenuBarShowSidebarTip": "一般太记小窗仅用于快速查看当前工作区,所以默认与主窗口工作区同步,不需要侧边栏,默认隐藏侧边栏。",
|
||||
"AttachToMenuBarTip": "创建一个点击菜单栏/任务栏图标会弹出的小太记窗口。提示:右键单击小图标以访问上下文菜单。",
|
||||
"AttachToTidgiMiniWindow": "附加到太记小窗",
|
||||
"AttachToTidgiMiniWindowShowSidebar": "太记小窗包含侧边栏",
|
||||
"AttachToTidgiMiniWindowShowSidebarTip": "一般太记小窗仅用于快速查看当前工作区,所以默认与主窗口工作区同步,不需要侧边栏,默认隐藏侧边栏。",
|
||||
"AttachToTidgiMiniWindowTip": "创建一个点击系统托盘图标会弹出的小太记窗口。提示:右键单击小图标以访问上下文菜单。",
|
||||
"AttachToTaskbar": "附加到任务栏",
|
||||
"AttachToTaskbarShowSidebar": "附加到任务栏的窗口包含侧边栏",
|
||||
"ChooseLanguage": "选择语言 Choose Language",
|
||||
|
|
@ -371,16 +371,16 @@
|
|||
"ItIsWorking": "好使的!",
|
||||
"Languages": "语言/Lang",
|
||||
"LightTheme": "亮色主题",
|
||||
"MenubarAlwaysOnTop": "保持菜单栏小窗口在其他窗口上方",
|
||||
"MenubarAlwaysOnTopDetail": "让太记的菜单栏小窗口永远保持在其它窗口上方,不会被其他窗口覆盖",
|
||||
"MenubarFixedWorkspace": "选择太记小窗固定展示的工作区",
|
||||
"MenubarShortcutKey": "设置切换开启太记小窗的快捷键",
|
||||
"MenubarShortcutKeyHelperText": "设置一个快捷键来快速打开或关闭太记小窗",
|
||||
"MenubarShortcutKeyPlaceholder": "例如:Ctrl+Shift+D",
|
||||
"MenubarSyncWorkspaceWithMainWindow": "小窗和主窗口展示同样的工作区",
|
||||
"MenubarSyncWorkspaceWithMainWindowDetail": "勾选后,小窗将与主窗口同步显示相同的工作区内容",
|
||||
"ShowMenubarWindowTitleBar": "小窗显示标题栏",
|
||||
"ShowMenubarWindowTitleBarDetail": "在菜单栏小窗口上显示可拖动的标题栏",
|
||||
"TidgiMiniWindowAlwaysOnTop": "保持太记小窗口在其他窗口上方",
|
||||
"TidgiMiniWindowAlwaysOnTopDetail": "让太记的小窗口永远保持在其它窗口上方,不会被其他窗口覆盖",
|
||||
"TidgiMiniWindowFixedWorkspace": "为固定的太记小窗口选择工作区",
|
||||
"TidgiMiniWindowShortcutKey": "设置快捷键来切换太记小窗口",
|
||||
"TidgiMiniWindowShortcutKeyHelperText": "设置一个快捷键来快速打开或关闭太记小窗口",
|
||||
"TidgiMiniWindowShortcutKeyPlaceholder": "例如:Ctrl+Shift+D",
|
||||
"TidgiMiniWindowSyncWorkspaceWithMainWindow": "小窗和主窗口展示同样的工作区",
|
||||
"TidgiMiniWindowSyncWorkspaceWithMainWindowDetail": "勾选后,小窗将与主窗口同步显示相同的工作区内容",
|
||||
"ShowTidgiMiniWindowTitleBar": "小窗显示标题栏",
|
||||
"ShowTidgiMiniWindowTitleBarDetail": "在太记小窗口上显示可拖动的标题栏",
|
||||
"Miscellaneous": "其他设置",
|
||||
"MoreWorkspaceSyncSettings": "更多工作区同步设置",
|
||||
"MoreWorkspaceSyncSettingsDescription": "请右键工作区图标,点右键菜单里的「编辑工作区」来打开工作区设置,在里面配各个工作区的同步设置。",
|
||||
|
|
@ -448,7 +448,7 @@
|
|||
"TestNotificationDescription": "<0>如果通知未显示,请确保在<1>macOS首选项 → 通知 → TidGi中启用通知</1></0>",
|
||||
"Theme": "主题色",
|
||||
"TiddlyWiki": "太微",
|
||||
"ToggleMenuBar": "切换显隐菜单栏",
|
||||
"ToggleTidgiMiniWindow": "切换太记小窗",
|
||||
"Token": "Git身份凭证",
|
||||
"TokenDescription": "用于向Git服务器验证身份并同步内容的凭证,可通过登录在线存储服务(如Github)来取得,也可以手动获取「Personal Access Token」后填到这里。",
|
||||
"Translatium": "翻译素APP",
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@
|
|||
"OpenCommandPalette": "打開搜索與命令面板",
|
||||
"OpenLinkInBrowser": "在瀏覽器中打開連結",
|
||||
"OpenTidGi": "打開太記",
|
||||
"OpenTidGiMenuBar": "打開太記小窗口",
|
||||
"OpenTidGiMiniWindow": "打開太記小窗口",
|
||||
"OpenWorkspaceInNewWindow": "在新窗口中打開工作區",
|
||||
"Paste": "黏貼",
|
||||
"Preferences": "設置...",
|
||||
|
|
@ -309,7 +309,7 @@
|
|||
"SelectNextWorkspace": "選擇下一個工作區",
|
||||
"SelectPreviousWorkspace": "選擇前一個工作區",
|
||||
"TidGi": "太記",
|
||||
"TidGiMenuBar": "太記小窗",
|
||||
"TidGiMiniWindow": "太記小窗",
|
||||
"View": "查看",
|
||||
"Wiki": "知識庫",
|
||||
"Window": "窗口",
|
||||
|
|
@ -324,10 +324,10 @@
|
|||
"AlwaysOnTopDetail": "讓太記的主窗口永遠保持在其它窗口上方,不會被其他窗口覆蓋",
|
||||
"AntiAntiLeech": "有的網站做了防盜鏈,會阻止某些圖片在你的知識庫上顯示,我們透過模擬訪問該網站的請求頭來繞過這種限制。",
|
||||
"AskDownloadLocation": "下載前詢問每個文件的保存位置",
|
||||
"AttachToMenuBar": "附加到選單欄",
|
||||
"AttachToMenuBarShowSidebar": "附加到選單欄的窗口包含側邊欄",
|
||||
"AttachToMenuBarShowSidebarTip": "一般太記小窗僅用於快速查看當前工作區,所以默認與主窗口工作區同步,不需要側邊欄,默認隱藏側邊欄。",
|
||||
"AttachToMenuBarTip": "創建一個點擊選單欄/任務欄圖示會彈出的小太記窗口。提示:右鍵單擊小圖示以訪問上下文菜單。",
|
||||
"AttachToTidgiMiniWindow": "附加到太記小窗",
|
||||
"AttachToTidgiMiniWindowShowSidebar": "太記小窗包含側邊欄",
|
||||
"AttachToTidgiMiniWindowShowSidebarTip": "一般太記小窗僅用於快速查看當前工作區,所以默認與主窗口工作區同步,不需要側邊欄,默認隱藏側邊欄。",
|
||||
"AttachToTidgiMiniWindowTip": "創建一個點擊系統托盤圖示會彈出的小太記窗口。提示:右鍵單擊小圖示以訪問上下文菜單。",
|
||||
"AttachToTaskbar": "附加到任務欄",
|
||||
"AttachToTaskbarShowSidebar": "附加到任務欄的窗口包含側邊欄",
|
||||
"ChooseLanguage": "選擇語言 Choose Language",
|
||||
|
|
@ -363,8 +363,8 @@
|
|||
"ItIsWorking": "好使的!",
|
||||
"Languages": "語言/Lang",
|
||||
"LightTheme": "亮色主題",
|
||||
"MenubarAlwaysOnTop": "保持選單欄小窗口在其他窗口上方",
|
||||
"MenubarAlwaysOnTopDetail": "讓太記的選單欄小窗口永遠保持在其它窗口上方,不會被其他窗口覆蓋",
|
||||
"TidgiMiniWindowAlwaysOnTop": "保持太記小窗口在其他窗口上方",
|
||||
"TidgiMiniWindowAlwaysOnTopDetail": "讓太記的小窗口永遠保持在其它窗口上方,不會被其他窗口覆蓋",
|
||||
"Miscellaneous": "其他設置",
|
||||
"MoreWorkspaceSyncSettings": "更多工作區同步設定",
|
||||
"MoreWorkspaceSyncSettingsDescription": "請右鍵工作區圖示,點右鍵菜單裡的「編輯工作區」來打開工作區設置,在裡面配各個工作區的同步設定。",
|
||||
|
|
@ -431,7 +431,7 @@
|
|||
"TestNotificationDescription": "<0>如果通知未顯示,請確保在<1>macOS首選項 → 通知 → TidGi中啟用通知</1></0>",
|
||||
"Theme": "主題色",
|
||||
"TiddlyWiki": "太微",
|
||||
"ToggleMenuBar": "切換顯隱選單欄",
|
||||
"ToggleTidgiMiniWindow": "切換太記小窗",
|
||||
"Token": "Git身份憑證",
|
||||
"TokenDescription": "用於向Git伺服器驗證身份並同步內容的憑證,可透過登錄在線儲存服務(如Github)來取得,也可以手動獲取「Personal Access Token」後填到這裡。",
|
||||
"Translatium": "翻譯素APP",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
"test": "pnpm run test:unit && pnpm run test:prepare-e2e && pnpm run test:e2e",
|
||||
"test:unit": "cross-env ELECTRON_RUN_AS_NODE=1 ./node_modules/.bin/electron ./node_modules/vitest/vitest.mjs run",
|
||||
"test:unit:coverage": "pnpm run test:unit --coverage",
|
||||
"test:prepare-e2e": "pnpm run clean && pnpm run build:plugin && cross-env NODE_ENV=test DEBUG=electron-forge:* electron-forge package",
|
||||
"test:prepare-e2e": "cross-env READ_DOC_BEFORE_USING='docs/Testing.md' && pnpm run clean && pnpm run build:plugin && cross-env NODE_ENV=test DEBUG=electron-forge:* electron-forge package",
|
||||
"test:e2e": "rimraf -- ./userData-test ./wiki-test && cross-env NODE_ENV=test pnpm dlx tsx scripts/developmentMkdir.ts && cross-env NODE_ENV=test cucumber-js --config features/cucumber.config.js",
|
||||
"make": "pnpm run build:plugin && cross-env NODE_ENV=production electron-forge make",
|
||||
"make:analyze": "cross-env ANALYZE=true pnpm run make",
|
||||
|
|
|
|||
|
|
@ -28,11 +28,11 @@ export const sourcePath = isPackaged
|
|||
export const buildResourcePath = path.resolve(sourcePath, 'build-resources');
|
||||
export const developmentImageFolderPath = path.resolve(sourcePath, 'images');
|
||||
|
||||
// Menubar icon
|
||||
const menuBarIconFileName = isMac ? 'menubarTemplate@2x.png' : 'menubar@2x.png';
|
||||
export const MENUBAR_ICON_PATH = isPackaged
|
||||
? path.resolve(process.resourcesPath, menuBarIconFileName) // Packaged: resources/<icon>
|
||||
: path.resolve(buildResourcePath, menuBarIconFileName); // Dev/Unit test: <project-root>/build-resources/<icon>
|
||||
// TidGi Mini Window icon
|
||||
const tidgiMiniWindowIconFileName = isMac ? 'tidgiMiniWindowTemplate@2x.png' : 'tidgiMiniWindow@2x.png';
|
||||
export const TIDGI_MINI_WINDOW_ICON_PATH = isPackaged
|
||||
? path.resolve(process.resourcesPath, tidgiMiniWindowIconFileName) // Packaged: resources/<icon>
|
||||
: path.resolve(buildResourcePath, tidgiMiniWindowIconFileName); // Dev/Unit test: <project-root>/build-resources/<icon>
|
||||
|
||||
// System paths
|
||||
export const CHROME_ERROR_PATH = 'chrome-error://chromewebdata/';
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ const commonInit = async (): Promise<void> => {
|
|||
externalAPIService.initialize(),
|
||||
]);
|
||||
|
||||
// if user want a menubar, we create a new window for that
|
||||
// if user want a tidgi mini window, we create a new window for that
|
||||
// handle workspace name + tiddler name in uri https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app
|
||||
deepLinkService.initializeDeepLink('tidgi');
|
||||
|
||||
|
|
@ -139,9 +139,9 @@ const commonInit = async (): Promise<void> => {
|
|||
await workspaceService.initializeDefaultPageWorkspaces();
|
||||
// perform wiki startup and git sync for each workspace
|
||||
await workspaceViewService.initializeAllWorkspaceView();
|
||||
const attachToMenubar = await preferenceService.get('attachToMenubar');
|
||||
if (attachToMenubar) {
|
||||
await windowService.openMenubarWindow(true, false);
|
||||
const attachToTidgiMiniWindow = await preferenceService.get('attachToTidgiMiniWindow');
|
||||
if (attachToTidgiMiniWindow) {
|
||||
await windowService.openTidgiMiniWindow(true, false);
|
||||
}
|
||||
|
||||
ipcMain.emit('request-update-pause-notifications-info');
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import Main from '../index';
|
|||
// Mock window.observables to provide realistic API behavior
|
||||
const preferencesSubject = new BehaviorSubject({
|
||||
sidebar: true,
|
||||
sidebarOnMenubar: true,
|
||||
sidebarOnTidgiMiniWindow: true,
|
||||
showSideBarText: true,
|
||||
showSideBarIcon: true,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export default function Main(): React.JSX.Element {
|
|||
const { t } = useTranslation();
|
||||
useInitialPage();
|
||||
const preferences = usePreferenceObservable();
|
||||
const showSidebar = (windowName === WindowNames.menuBar ? preferences?.sidebarOnMenubar : preferences?.sidebar) ?? true;
|
||||
const showSidebar = (windowName === WindowNames.tidgiMiniWindow ? preferences?.sidebarOnTidgiMiniWindow : preferences?.sidebar) ?? true;
|
||||
return (
|
||||
<OuterRoot>
|
||||
<Helmet>
|
||||
|
|
|
|||
|
|
@ -19,14 +19,14 @@ export function useInitialPage() {
|
|||
|
||||
let targetWorkspace = workspacesList.find(workspace => workspace.active);
|
||||
|
||||
// For menubar window, determine which workspace to show based on preferences
|
||||
if (windowName === WindowNames.menuBar && preferences) {
|
||||
const { menubarSyncWorkspaceWithMainWindow, menubarFixedWorkspaceId } = preferences;
|
||||
const shouldSync = menubarSyncWorkspaceWithMainWindow === undefined || menubarSyncWorkspaceWithMainWindow;
|
||||
// For tidgi mini window, determine which workspace to show based on preferences
|
||||
if (windowName === WindowNames.tidgiMiniWindow && preferences) {
|
||||
const { tidgiMiniWindowSyncWorkspaceWithMainWindow, tidgiMiniWindowFixedWorkspaceId } = preferences;
|
||||
const shouldSync = tidgiMiniWindowSyncWorkspaceWithMainWindow === undefined || tidgiMiniWindowSyncWorkspaceWithMainWindow;
|
||||
|
||||
if (!shouldSync && menubarFixedWorkspaceId) {
|
||||
// Use fixed workspace if not syncing
|
||||
const fixedWorkspace = workspacesList.find(ws => ws.id === menubarFixedWorkspaceId);
|
||||
if (!shouldSync && tidgiMiniWindowFixedWorkspaceId) {
|
||||
// If not syncing with main window, use fixed workspace
|
||||
const fixedWorkspace = workspacesList.find(ws => ws.id === tidgiMiniWindowFixedWorkspaceId);
|
||||
if (fixedWorkspace) {
|
||||
targetWorkspace = fixedWorkspace;
|
||||
}
|
||||
|
|
@ -51,19 +51,19 @@ export function useInitialPage() {
|
|||
}
|
||||
}, [location, workspacesList, preferences, windowName, setLocation]);
|
||||
|
||||
// For menubar window, also listen to active workspace changes
|
||||
// For tidgi mini window, also listen to active workspace changes
|
||||
useEffect(() => {
|
||||
if (windowName !== WindowNames.menuBar || !workspacesList || !preferences) {
|
||||
if (windowName !== WindowNames.tidgiMiniWindow || !workspacesList || !preferences) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { menubarSyncWorkspaceWithMainWindow, menubarFixedWorkspaceId } = preferences;
|
||||
const shouldSync = menubarSyncWorkspaceWithMainWindow === undefined || menubarSyncWorkspaceWithMainWindow;
|
||||
const { tidgiMiniWindowSyncWorkspaceWithMainWindow, tidgiMiniWindowFixedWorkspaceId } = preferences;
|
||||
const shouldSync = tidgiMiniWindowSyncWorkspaceWithMainWindow === undefined || tidgiMiniWindowSyncWorkspaceWithMainWindow;
|
||||
|
||||
// Determine target workspace
|
||||
let targetWorkspace = workspacesList.find(workspace => workspace.active);
|
||||
if (!shouldSync && menubarFixedWorkspaceId) {
|
||||
const fixedWorkspace = workspacesList.find(ws => ws.id === menubarFixedWorkspaceId);
|
||||
if (!shouldSync && tidgiMiniWindowFixedWorkspaceId) {
|
||||
const fixedWorkspace = workspacesList.find(ws => ws.id === tidgiMiniWindowFixedWorkspaceId);
|
||||
if (fixedWorkspace) {
|
||||
targetWorkspace = fixedWorkspace;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export const remoteMethods = {
|
|||
* @returns — the index of the clicked button. -1 means unknown or errored. 0 if canceled (this can be configured by `cancelId` in the options).
|
||||
*/
|
||||
showElectronMessageBoxSync: (options: Electron.MessageBoxSyncOptions): number => {
|
||||
// only main window can show message box, view window (browserView) can't. Currently didn't handle menubar window, hope it won't show message box...
|
||||
// only main window can show message box, view window (browserView) can't. Currently didn't handle tidgi mini window, hope it won't show message box...
|
||||
const clickedButtonIndex = ipcRenderer.sendSync(NativeChannel.showElectronMessageBoxSync, options, WindowNames.main) as unknown;
|
||||
if (typeof clickedButtonIndex === 'number') {
|
||||
return clickedButtonIndex;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export interface IPaths {
|
|||
LOCALIZATION_FOLDER: string;
|
||||
LOG_FOLDER: string;
|
||||
MAIN_WINDOW_WEBPACK_ENTRY: string;
|
||||
MENUBAR_ICON_PATH: string;
|
||||
TIDGI_MINI_WINDOW_ICON_PATH: string;
|
||||
SETTINGS_FOLDER: string;
|
||||
TIDDLERS_PATH: string;
|
||||
TIDDLYWIKI_TEMPLATE_FOLDER_PATH: string;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export class DatabaseService implements IDatabaseService {
|
|||
hasContent: !!this.settingFileContent,
|
||||
keys: this.settingFileContent ? Object.keys(this.settingFileContent).length : 0,
|
||||
hasPreferences: !!this.settingFileContent?.preferences,
|
||||
attachToMenubar: this.settingFileContent?.preferences?.attachToMenubar,
|
||||
attachToTidgiMiniWindow: this.settingFileContent?.preferences?.attachToTidgiMiniWindow,
|
||||
settingsFilePath: settings.file(),
|
||||
function: 'DatabaseService.initializeForApp',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ export default async function getViewBounds(
|
|||
): Promise<{ height: number; width: number; x: number; y: number }> {
|
||||
const { findInPage = false, windowName } = config;
|
||||
const preferencesService = container.get<IPreferenceService>(serviceIdentifier.Preference);
|
||||
const [sidebar, sidebarOnMenubar] = await Promise.all([preferencesService.get('sidebar'), preferencesService.get('sidebarOnMenubar')]);
|
||||
const showSidebar = windowName === WindowNames.menuBar ? sidebarOnMenubar : sidebar;
|
||||
const [sidebar, sidebarOnTidgiMiniWindow] = await Promise.all([preferencesService.get('sidebar'), preferencesService.get('sidebarOnTidgiMiniWindow')]);
|
||||
const showSidebar = windowName === WindowNames.tidgiMiniWindow ? sidebarOnTidgiMiniWindow : sidebar;
|
||||
// Now showing sidebar on secondary window
|
||||
const secondary = windowName === WindowNames.secondary;
|
||||
const x = (showSidebar && !secondary) ? 68 : 0;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export const defaultPreferences: IPreferences = {
|
|||
allowPrerelease: Boolean(semver.prerelease(app.getVersion())),
|
||||
alwaysOnTop: false,
|
||||
askForDownloadPath: true,
|
||||
attachToMenubar: false,
|
||||
attachToTidgiMiniWindow: false,
|
||||
disableAntiAntiLeech: false,
|
||||
disableAntiAntiLeechForUrls: [],
|
||||
downloadPath: DEFAULT_DOWNLOADS_PATH,
|
||||
|
|
@ -16,10 +16,10 @@ export const defaultPreferences: IPreferences = {
|
|||
hideMenuBar: false,
|
||||
ignoreCertificateErrors: false,
|
||||
language: 'zh-Hans',
|
||||
menuBarAlwaysOnTop: false,
|
||||
menubarFixedWorkspaceId: '',
|
||||
menubarSyncWorkspaceWithMainWindow: true,
|
||||
showMenubarWindowTitleBar: false,
|
||||
tidgiMiniWindowAlwaysOnTop: false,
|
||||
tidgiMiniWindowFixedWorkspaceId: '',
|
||||
tidgiMiniWindowSyncWorkspaceWithMainWindow: true,
|
||||
showTidgiMiniWindowTitleBar: false,
|
||||
keyboardShortcuts: {},
|
||||
pauseNotifications: '',
|
||||
pauseNotificationsBySchedule: false,
|
||||
|
|
@ -32,7 +32,7 @@ export const defaultPreferences: IPreferences = {
|
|||
showSideBarIcon: true,
|
||||
showSideBarText: true,
|
||||
sidebar: true,
|
||||
sidebarOnMenubar: false,
|
||||
sidebarOnTidgiMiniWindow: false,
|
||||
spellcheck: true,
|
||||
spellcheckLanguages: ['en-US'],
|
||||
swipeToNavigate: true,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export interface IPreferences {
|
|||
allowPrerelease: boolean;
|
||||
alwaysOnTop: boolean;
|
||||
askForDownloadPath: boolean;
|
||||
attachToMenubar: boolean;
|
||||
attachToTidgiMiniWindow: boolean;
|
||||
/**
|
||||
* 完全关闭反盗链
|
||||
*/
|
||||
|
|
@ -26,7 +26,7 @@ export interface IPreferences {
|
|||
hideMenuBar: boolean;
|
||||
ignoreCertificateErrors: boolean;
|
||||
language: string;
|
||||
menuBarAlwaysOnTop: boolean;
|
||||
tidgiMiniWindowAlwaysOnTop: boolean;
|
||||
pauseNotifications: string | undefined;
|
||||
pauseNotificationsBySchedule: boolean;
|
||||
pauseNotificationsByScheduleFrom: string;
|
||||
|
|
@ -42,24 +42,24 @@ export interface IPreferences {
|
|||
*/
|
||||
sidebar: boolean;
|
||||
/**
|
||||
* Should show sidebar on menubar window?
|
||||
* Should show sidebar on tidgi mini window?
|
||||
*/
|
||||
sidebarOnMenubar: boolean;
|
||||
sidebarOnTidgiMiniWindow: boolean;
|
||||
spellcheck: boolean;
|
||||
spellcheckLanguages: HunspellLanguages[];
|
||||
swipeToNavigate: boolean;
|
||||
/**
|
||||
* Whether menubar window should show the same workspace as main window
|
||||
*/
|
||||
menubarSyncWorkspaceWithMainWindow: boolean;
|
||||
tidgiMiniWindowSyncWorkspaceWithMainWindow: boolean;
|
||||
/**
|
||||
* The workspace ID that menubar window should always show when menubarSyncWorkspaceWithMainWindow is false
|
||||
* The workspace ID that tidgi mini window should always show when tidgiMiniWindowSyncWorkspaceWithMainWindow is false
|
||||
*/
|
||||
menubarFixedWorkspaceId: string | undefined;
|
||||
tidgiMiniWindowFixedWorkspaceId: string | undefined;
|
||||
/**
|
||||
* Whether to show title bar on menubar window (independent of main window's titleBar setting)
|
||||
* Whether to show title bar on tidgi mini window (independent of main window's titleBar setting)
|
||||
*/
|
||||
showMenubarWindowTitleBar: boolean;
|
||||
showTidgiMiniWindowTitleBar: boolean;
|
||||
/**
|
||||
* Keyboard shortcuts configuration stored as serviceIdentifier.methodName -> shortcut
|
||||
*/
|
||||
|
|
@ -82,7 +82,7 @@ export enum PreferenceSections {
|
|||
friendLinks = 'friendLinks',
|
||||
general = 'general',
|
||||
languages = 'languages',
|
||||
menubar = 'menubar',
|
||||
tidgiMiniWindow = 'tidgiMiniWindow',
|
||||
misc = 'misc',
|
||||
network = 'network',
|
||||
notifications = 'notifications',
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ export class View implements IViewService {
|
|||
};
|
||||
const checkNotExistResult = await Promise.all([
|
||||
checkNotExist(workspace, WindowNames.main),
|
||||
this.preferenceService.get('attachToMenubar').then((attachToMenubar) => attachToMenubar && checkNotExist(workspace, WindowNames.menuBar)),
|
||||
this.preferenceService.get('attachToTidgiMiniWindow').then((attachToTidgiMiniWindow) => attachToTidgiMiniWindow && checkNotExist(workspace, WindowNames.tidgiMiniWindow)),
|
||||
]);
|
||||
return checkNotExistResult.every((result) => !result);
|
||||
}
|
||||
|
|
@ -315,8 +315,8 @@ export class View implements IViewService {
|
|||
// Add view to window if:
|
||||
// 1. workspace is active (main window)
|
||||
// 2. windowName is secondary (always add)
|
||||
// 3. windowName is menuBar (menubar can have fixed workspace independent of main window's active workspace)
|
||||
if (workspace.active || windowName === WindowNames.secondary || windowName === WindowNames.menuBar) {
|
||||
// 3. windowName is tidgiMiniWindow (tidgi mini window can have fixed workspace independent of main window's active workspace)
|
||||
if (workspace.active || windowName === WindowNames.secondary || windowName === WindowNames.tidgiMiniWindow) {
|
||||
browserWindow.contentView.addChildView(view);
|
||||
const contentSize = browserWindow.getContentSize();
|
||||
const newViewBounds = await getViewBounds(contentSize as [number, number], { windowName });
|
||||
|
|
@ -330,7 +330,7 @@ export class View implements IViewService {
|
|||
if (updatedWorkspace === undefined) return;
|
||||
// Prevent update non-active (hiding) wiki workspace, so it won't pop up to cover other active agent workspace
|
||||
if (windowName === WindowNames.main && !updatedWorkspace.active) return;
|
||||
if ([WindowNames.secondary, WindowNames.main, WindowNames.menuBar].includes(windowName)) {
|
||||
if ([WindowNames.secondary, WindowNames.main, WindowNames.tidgiMiniWindow].includes(windowName)) {
|
||||
const contentSize = browserWindow.getContentSize();
|
||||
const newViewBounds = await getViewBounds(contentSize as [number, number], { windowName });
|
||||
view.setBounds(newViewBounds);
|
||||
|
|
@ -412,36 +412,36 @@ export class View implements IViewService {
|
|||
public async setActiveViewForAllBrowserViews(workspaceID: string): Promise<void> {
|
||||
// Set main window workspace
|
||||
const mainWindowTask = this.setActiveView(workspaceID, WindowNames.main);
|
||||
const [attachToMenubar, menubarSyncWorkspaceWithMainWindow, menubarFixedWorkspaceId] = await Promise.all([
|
||||
this.preferenceService.get('attachToMenubar'),
|
||||
this.preferenceService.get('menubarSyncWorkspaceWithMainWindow'),
|
||||
this.preferenceService.get('menubarFixedWorkspaceId'),
|
||||
const [attachToTidgiMiniWindow, tidgiMiniWindowSyncWorkspaceWithMainWindow, tidgiMiniWindowFixedWorkspaceId] = await Promise.all([
|
||||
this.preferenceService.get('attachToTidgiMiniWindow'),
|
||||
this.preferenceService.get('tidgiMiniWindowSyncWorkspaceWithMainWindow'),
|
||||
this.preferenceService.get('tidgiMiniWindowFixedWorkspaceId'),
|
||||
]);
|
||||
|
||||
// For menubar window, decide which workspace to show based on preferences
|
||||
let menubarTask = Promise.resolve();
|
||||
if (attachToMenubar) {
|
||||
// For tidgi mini window, decide which workspace to show based on preferences
|
||||
let tidgiMiniWindowTask = Promise.resolve();
|
||||
if (attachToTidgiMiniWindow) {
|
||||
// Default to sync (undefined or true), otherwise use fixed workspace ID (fallback to main if not set)
|
||||
const shouldSync = menubarSyncWorkspaceWithMainWindow === undefined || menubarSyncWorkspaceWithMainWindow;
|
||||
const menubarWorkspaceId = shouldSync ? workspaceID : (menubarFixedWorkspaceId || workspaceID);
|
||||
const shouldSync = tidgiMiniWindowSyncWorkspaceWithMainWindow === undefined || tidgiMiniWindowSyncWorkspaceWithMainWindow;
|
||||
const tidgiMiniWindowWorkspaceId = shouldSync ? workspaceID : (tidgiMiniWindowFixedWorkspaceId || workspaceID);
|
||||
|
||||
// Check if the target workspace is a pageType workspace (which doesn't have a view)
|
||||
if (!shouldSync && menubarFixedWorkspaceId) {
|
||||
const fixedWorkspace = await this.workspaceService.get(menubarFixedWorkspaceId);
|
||||
if (!shouldSync && tidgiMiniWindowFixedWorkspaceId) {
|
||||
const fixedWorkspace = await this.workspaceService.get(tidgiMiniWindowFixedWorkspaceId);
|
||||
if (fixedWorkspace?.pageType) {
|
||||
logger.debug(
|
||||
`setActiveViewForAllBrowserViews: skip menu bar window because fixed workspace ${menubarFixedWorkspaceId} is a page type workspace.`,
|
||||
`setActiveViewForAllBrowserViews: skip tidgi mini window because fixed workspace ${tidgiMiniWindowFixedWorkspaceId} is a page type workspace.`,
|
||||
);
|
||||
// Don't set any view for pageType workspaces - menubar task remains as Promise.resolve()
|
||||
// Don't set any view for pageType workspaces - tidgi mini window task remains as Promise.resolve()
|
||||
} else {
|
||||
menubarTask = this.setActiveView(menubarWorkspaceId, WindowNames.menuBar);
|
||||
tidgiMiniWindowTask = this.setActiveView(tidgiMiniWindowWorkspaceId, WindowNames.tidgiMiniWindow);
|
||||
}
|
||||
} else {
|
||||
menubarTask = this.setActiveView(menubarWorkspaceId, WindowNames.menuBar);
|
||||
tidgiMiniWindowTask = this.setActiveView(tidgiMiniWindowWorkspaceId, WindowNames.tidgiMiniWindow);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([mainWindowTask, menubarTask]);
|
||||
await Promise.all([mainWindowTask, tidgiMiniWindowTask]);
|
||||
}
|
||||
|
||||
public async setActiveView(workspaceID: string, windowName: WindowNames): Promise<void> {
|
||||
|
|
@ -611,9 +611,9 @@ export class View implements IViewService {
|
|||
const workspace = await workspaceService.getActiveWorkspace();
|
||||
if (workspace !== undefined) {
|
||||
const windowService = container.get<IWindowService>(serviceIdentifier.Window);
|
||||
const isMenubarOpen = await windowService.isMenubarOpen();
|
||||
if (isMenubarOpen) {
|
||||
return this.getView(workspace.id, WindowNames.menuBar);
|
||||
const isTidgiMiniWindowOpen = await windowService.isTidgiMiniWindowOpen();
|
||||
if (isTidgiMiniWindowOpen) {
|
||||
return this.getView(workspace.id, WindowNames.tidgiMiniWindow);
|
||||
} else {
|
||||
return this.getView(workspace.id, WindowNames.main);
|
||||
}
|
||||
|
|
@ -624,7 +624,7 @@ export class View implements IViewService {
|
|||
const workspaceService = container.get<IWorkspaceService>(serviceIdentifier.Workspace);
|
||||
const workspace = await workspaceService.getActiveWorkspace();
|
||||
if (workspace !== undefined) {
|
||||
return [this.getView(workspace.id, WindowNames.main), this.getView(workspace.id, WindowNames.menuBar)];
|
||||
return [this.getView(workspace.id, WindowNames.main), this.getView(workspace.id, WindowNames.tidgiMiniWindow)];
|
||||
}
|
||||
logger.error(`getActiveBrowserViews workspace !== undefined`, { stack: new Error('stack').stack?.replace('Error:', '') });
|
||||
return [];
|
||||
|
|
|
|||
|
|
@ -30,11 +30,11 @@ export interface IViewService {
|
|||
createViewAddToWindow(workspace: IWorkspace, browserWindow: BrowserWindow, sharedWebPreferences: WebPreferences, windowName: WindowNames): Promise<WebContentsView>;
|
||||
forEachView: (functionToRun: (view: WebContentsView, workspaceID: string, windowName: WindowNames) => void) => void;
|
||||
/**
|
||||
* If menubar is open, we get menubar browser view, else we get main window browser view
|
||||
* If tidgi mini window is open, we get tidgi mini window browser view, else we get main window browser view
|
||||
*/
|
||||
getActiveBrowserView: () => Promise<WebContentsView | undefined>;
|
||||
/**
|
||||
* Get active workspace's main window and menubar browser view.
|
||||
* Get active workspace's main window and tidgi mini window browser view.
|
||||
*/
|
||||
getActiveBrowserViews: () => Promise<Array<WebContentsView | undefined>>;
|
||||
getLoadedViewEnsure(workspaceID: string, windowName: WindowNames): Promise<WebContentsView>;
|
||||
|
|
@ -74,7 +74,7 @@ export interface IViewService {
|
|||
/**
|
||||
* Bring an already created view to the front. If it happened to not created, will call `addView()` to create one.
|
||||
* @param workspaceID id, can only be main workspace id, because only main workspace will have view created.
|
||||
* @param windowName you can control main window or menubar window to have this view.
|
||||
* @param windowName you can control main window or tidgi mini window to have this view.
|
||||
* @returns
|
||||
*/
|
||||
setActiveView: (workspaceID: string, windowName: WindowNames) => Promise<void>;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export enum WindowNames {
|
|||
* We only have a single instance of main window, that is the app window.
|
||||
*/
|
||||
main = 'main',
|
||||
menuBar = 'menuBar',
|
||||
tidgiMiniWindow = 'tidgiMiniWindow',
|
||||
notifications = 'notifications',
|
||||
preferences = 'preferences',
|
||||
/**
|
||||
|
|
@ -46,7 +46,7 @@ export const windowDimension: Record<WindowNames, { height?: number; width?: num
|
|||
width: 1200,
|
||||
height: 768,
|
||||
},
|
||||
[WindowNames.menuBar]: {
|
||||
[WindowNames.tidgiMiniWindow]: {
|
||||
width: 500,
|
||||
height: 600,
|
||||
},
|
||||
|
|
@ -100,7 +100,7 @@ export interface WindowMeta {
|
|||
[WindowNames.auth]: undefined;
|
||||
[WindowNames.editWorkspace]: { workspaceID?: string };
|
||||
[WindowNames.main]: { forceClose?: boolean };
|
||||
[WindowNames.menuBar]: undefined;
|
||||
[WindowNames.tidgiMiniWindow]: undefined;
|
||||
[WindowNames.notifications]: undefined;
|
||||
[WindowNames.preferences]: IPreferenceWindowMeta;
|
||||
[WindowNames.spellcheck]: undefined;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { MENUBAR_ICON_PATH } from '@/constants/paths';
|
||||
import { TIDGI_MINI_WINDOW_ICON_PATH } from '@/constants/paths';
|
||||
import { isMac } from '@/helpers/system';
|
||||
import { container } from '@services/container';
|
||||
import { i18n } from '@services/libs/i18n';
|
||||
|
|
@ -15,14 +15,17 @@ import type { IWindowService } from './interface';
|
|||
import { getMainWindowEntry } from './viteEntry';
|
||||
import { WindowNames } from './WindowProperties';
|
||||
|
||||
export async function handleAttachToMenuBar(windowConfig: BrowserWindowConstructorOptions, windowWithBrowserViewState: windowStateKeeper.State | undefined): Promise<Menubar> {
|
||||
export async function handleAttachToTidgiMiniWindow(
|
||||
windowConfig: BrowserWindowConstructorOptions,
|
||||
windowWithBrowserViewState: windowStateKeeper.State | undefined,
|
||||
): Promise<Menubar> {
|
||||
const menuService = container.get<IMenuService>(serviceIdentifier.MenuService);
|
||||
const windowService = container.get<IWindowService>(serviceIdentifier.Window);
|
||||
const viewService = container.get<IViewService>(serviceIdentifier.View);
|
||||
const preferenceService = container.get<IPreferenceService>(serviceIdentifier.Preference);
|
||||
|
||||
// Get menubar-specific titleBar preference
|
||||
const showMenubarWindowTitleBar = await preferenceService.get('showMenubarWindowTitleBar');
|
||||
// Get tidgi mini window-specific titleBar preference
|
||||
const showTidgiMiniWindowTitleBar = await preferenceService.get('showTidgiMiniWindowTitleBar');
|
||||
|
||||
// setImage after Tray instance is created to avoid
|
||||
// "Segmentation fault (core dumped)" bug on Linux
|
||||
|
|
@ -30,71 +33,71 @@ export async function handleAttachToMenuBar(windowConfig: BrowserWindowConstruct
|
|||
// https://github.com/atomery/translatium/issues/164
|
||||
const tray = new Tray(nativeImage.createEmpty());
|
||||
// icon template is not supported on Windows & Linux
|
||||
tray.setImage(MENUBAR_ICON_PATH);
|
||||
tray.setImage(nativeImage.createFromPath(TIDGI_MINI_WINDOW_ICON_PATH));
|
||||
|
||||
// Create menubar-specific window configuration
|
||||
// Override titleBar settings from windowConfig with menubar-specific preference
|
||||
const menubarWindowConfig: BrowserWindowConstructorOptions = {
|
||||
// Create tidgi mini window-specific window configuration
|
||||
// Override titleBar settings from windowConfig with tidgi mini window-specific preference
|
||||
const tidgiMiniWindowConfig: BrowserWindowConstructorOptions = {
|
||||
...windowConfig,
|
||||
show: false,
|
||||
minHeight: 100,
|
||||
minWidth: 250,
|
||||
// Use menubar-specific titleBar setting instead of inheriting from main window
|
||||
titleBarStyle: showMenubarWindowTitleBar ? 'default' : 'hidden',
|
||||
frame: showMenubarWindowTitleBar,
|
||||
// Use tidgi mini window-specific titleBar setting instead of inheriting from main window
|
||||
titleBarStyle: showTidgiMiniWindowTitleBar ? 'default' : 'hidden',
|
||||
frame: showTidgiMiniWindowTitleBar,
|
||||
// Always hide the menu bar (File, Edit, View menu), even when showing title bar
|
||||
autoHideMenuBar: true,
|
||||
};
|
||||
|
||||
logger.info('Creating menubar with titleBar configuration', {
|
||||
function: 'handleAttachToMenuBar',
|
||||
showMenubarWindowTitleBar,
|
||||
titleBarStyle: menubarWindowConfig.titleBarStyle,
|
||||
frame: menubarWindowConfig.frame,
|
||||
logger.info('Creating tidgi mini window with titleBar configuration', {
|
||||
function: 'handleAttachToTidgiMiniWindow',
|
||||
showTidgiMiniWindowTitleBar,
|
||||
titleBarStyle: tidgiMiniWindowConfig.titleBarStyle,
|
||||
frame: tidgiMiniWindowConfig.frame,
|
||||
});
|
||||
|
||||
const menuBar = menubar({
|
||||
const tidgiMiniWindow = menubar({
|
||||
index: getMainWindowEntry(),
|
||||
tray,
|
||||
activateWithApp: false,
|
||||
showDockIcon: true,
|
||||
preloadWindow: true,
|
||||
tooltip: i18n.t('Menu.TidGiMenuBar'),
|
||||
browserWindow: menubarWindowConfig,
|
||||
tooltip: i18n.t('Menu.TidGiMiniWindow'),
|
||||
browserWindow: tidgiMiniWindowConfig,
|
||||
});
|
||||
|
||||
menuBar.on('after-create-window', () => {
|
||||
if (menuBar.window !== undefined) {
|
||||
menuBar.window.on('focus', async () => {
|
||||
logger.debug('restore window position', { function: 'handleAttachToMenuBar' });
|
||||
tidgiMiniWindow.on('after-create-window', () => {
|
||||
if (tidgiMiniWindow.window !== undefined) {
|
||||
tidgiMiniWindow.window.on('focus', async () => {
|
||||
logger.debug('restore window position', { function: 'handleAttachToTidgiMiniWindow' });
|
||||
if (windowWithBrowserViewState === undefined) {
|
||||
logger.debug('windowWithBrowserViewState is undefined for menuBar', { function: 'handleAttachToMenuBar' });
|
||||
logger.debug('windowWithBrowserViewState is undefined for tidgiMiniWindow', { function: 'handleAttachToTidgiMiniWindow' });
|
||||
} else {
|
||||
if (menuBar.window === undefined) {
|
||||
logger.debug('menuBar.window is undefined', { function: 'handleAttachToMenuBar' });
|
||||
if (tidgiMiniWindow.window === undefined) {
|
||||
logger.debug('tidgiMiniWindow.window is undefined', { function: 'handleAttachToTidgiMiniWindow' });
|
||||
} else {
|
||||
const haveXYValue = [windowWithBrowserViewState.x, windowWithBrowserViewState.y].every((value) => Number.isFinite(value));
|
||||
const haveWHValue = [windowWithBrowserViewState.width, windowWithBrowserViewState.height].every((value) => Number.isFinite(value));
|
||||
if (haveXYValue) {
|
||||
menuBar.window.setPosition(windowWithBrowserViewState.x, windowWithBrowserViewState.y, false);
|
||||
tidgiMiniWindow.window.setPosition(windowWithBrowserViewState.x, windowWithBrowserViewState.y, false);
|
||||
}
|
||||
if (haveWHValue) {
|
||||
menuBar.window.setSize(windowWithBrowserViewState.width, windowWithBrowserViewState.height, false);
|
||||
tidgiMiniWindow.window.setSize(windowWithBrowserViewState.width, windowWithBrowserViewState.height, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
const view = await viewService.getActiveBrowserView();
|
||||
view?.webContents.focus();
|
||||
});
|
||||
menuBar.window.removeAllListeners('close');
|
||||
menuBar.window.on('close', (event) => {
|
||||
tidgiMiniWindow.window.removeAllListeners('close');
|
||||
tidgiMiniWindow.window.on('close', (event) => {
|
||||
event.preventDefault();
|
||||
menuBar.hideWindow();
|
||||
tidgiMiniWindow.hideWindow();
|
||||
});
|
||||
}
|
||||
});
|
||||
menuBar.on('hide', async () => {
|
||||
// on mac, calling `menuBar.app.hide()` with main window open will bring background main window up, which we don't want. We want to bring previous other app up. So close main window first.
|
||||
tidgiMiniWindow.on('hide', async () => {
|
||||
// on mac, calling `tidgiMiniWindow.app.hide()` with main window open will bring background main window up, which we don't want. We want to bring previous other app up. So close main window first.
|
||||
if (isMac) {
|
||||
const mainWindow = windowService.get(WindowNames.main);
|
||||
if (mainWindow?.isVisible() === true) {
|
||||
|
|
@ -103,29 +106,29 @@ export async function handleAttachToMenuBar(windowConfig: BrowserWindowConstruct
|
|||
}
|
||||
});
|
||||
// https://github.com/maxogden/menubar/issues/120
|
||||
menuBar.on('after-hide', () => {
|
||||
tidgiMiniWindow.on('after-hide', () => {
|
||||
if (isMac) {
|
||||
menuBar.app.hide();
|
||||
tidgiMiniWindow.app.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// manually save window state https://github.com/mawie81/electron-window-state/issues/64
|
||||
const debouncedSaveWindowState = debounce(
|
||||
() => {
|
||||
if (menuBar.window !== undefined) {
|
||||
windowWithBrowserViewState?.saveState(menuBar.window);
|
||||
if (tidgiMiniWindow.window !== undefined) {
|
||||
windowWithBrowserViewState?.saveState(tidgiMiniWindow.window);
|
||||
}
|
||||
},
|
||||
500,
|
||||
);
|
||||
// menubar is hide, not close, so not managed by windowStateKeeper, need to save manually
|
||||
menuBar.window?.on('resize', debouncedSaveWindowState);
|
||||
menuBar.window?.on('move', debouncedSaveWindowState);
|
||||
// tidgi mini window is hide, not close, so not managed by windowStateKeeper, need to save manually
|
||||
tidgiMiniWindow.window?.on('resize', debouncedSaveWindowState);
|
||||
tidgiMiniWindow.window?.on('move', debouncedSaveWindowState);
|
||||
|
||||
return await new Promise<Menubar>((resolve) => {
|
||||
menuBar.on('ready', async () => {
|
||||
tidgiMiniWindow.on('ready', async () => {
|
||||
// right on tray icon
|
||||
menuBar.tray.on('right-click', () => {
|
||||
tidgiMiniWindow.tray.on('right-click', () => {
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: i18n.t('ContextMenu.OpenTidGi'),
|
||||
|
|
@ -134,9 +137,9 @@ export async function handleAttachToMenuBar(windowConfig: BrowserWindowConstruct
|
|||
},
|
||||
},
|
||||
{
|
||||
label: i18n.t('ContextMenu.OpenTidGiMenuBar'),
|
||||
label: i18n.t('ContextMenu.OpenTidGiMiniWindow'),
|
||||
click: async () => {
|
||||
await menuBar.showWindow();
|
||||
await tidgiMiniWindow.showWindow();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -165,23 +168,23 @@ export async function handleAttachToMenuBar(windowConfig: BrowserWindowConstruct
|
|||
{
|
||||
label: i18n.t('ContextMenu.Quit'),
|
||||
click: () => {
|
||||
menuBar.app.quit();
|
||||
tidgiMiniWindow.app.quit();
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
menuBar.tray.popUpContextMenu(contextMenu);
|
||||
tidgiMiniWindow.tray.popUpContextMenu(contextMenu);
|
||||
});
|
||||
|
||||
// right click on window content
|
||||
if (menuBar.window?.webContents !== undefined) {
|
||||
const unregisterContextMenu = await menuService.initContextMenuForWindowWebContents(menuBar.window.webContents);
|
||||
menuBar.on('after-close', () => {
|
||||
if (tidgiMiniWindow.window?.webContents !== undefined) {
|
||||
const unregisterContextMenu = await menuService.initContextMenuForWindowWebContents(tidgiMiniWindow.window.webContents);
|
||||
tidgiMiniWindow.on('after-close', () => {
|
||||
unregisterContextMenu();
|
||||
});
|
||||
}
|
||||
|
||||
resolve(menuBar);
|
||||
resolve(tidgiMiniWindow);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ import { container } from '@services/container';
|
|||
import getViewBounds from '@services/libs/getViewBounds';
|
||||
import { logger } from '@services/libs/log';
|
||||
import type { IThemeService } from '@services/theme/interface';
|
||||
import { handleAttachToMenuBar } from './handleAttachToMenuBar';
|
||||
import { handleAttachToTidgiMiniWindow } from './handleAttachToTidgiMiniWindow';
|
||||
import { handleCreateBasicWindow } from './handleCreateBasicWindow';
|
||||
import type { IWindowOpenConfig, IWindowService } from './interface';
|
||||
import { registerBrowserViewWindowListeners } from './registerBrowserViewWindowListeners';
|
||||
|
|
@ -32,8 +32,8 @@ import { getPreloadPath } from './viteEntry';
|
|||
export class Window implements IWindowService {
|
||||
private readonly windows = new Map<WindowNames, BrowserWindow>();
|
||||
private windowMeta = {} as Partial<WindowMeta>;
|
||||
/** menubar version of main window, if user set openInMenubar to true in preferences */
|
||||
private mainWindowMenuBar?: Menubar;
|
||||
/** tidgi mini window version of main window, if user set attachToTidgiMiniWindow to true in preferences */
|
||||
private tidgiMiniWindowMenubar?: Menubar;
|
||||
|
||||
constructor(
|
||||
@inject(serviceIdentifier.Preference) private readonly preferenceService: IPreferenceService,
|
||||
|
|
@ -77,8 +77,8 @@ export class Window implements IWindowService {
|
|||
}
|
||||
|
||||
public get(windowName: WindowNames = WindowNames.main): BrowserWindow | undefined {
|
||||
if (windowName === WindowNames.menuBar) {
|
||||
return this.mainWindowMenuBar?.window;
|
||||
if (windowName === WindowNames.tidgiMiniWindow) {
|
||||
return this.tidgiMiniWindowMenubar?.window;
|
||||
}
|
||||
return this.windows.get(windowName);
|
||||
}
|
||||
|
|
@ -125,8 +125,8 @@ export class Window implements IWindowService {
|
|||
this.windows.clear();
|
||||
}
|
||||
|
||||
public async isMenubarOpen(): Promise<boolean> {
|
||||
return this.mainWindowMenuBar?.window?.isVisible() ?? false;
|
||||
public async isTidgiMiniWindowOpen(): Promise<boolean> {
|
||||
return this.tidgiMiniWindowMenubar?.window?.isVisible() ?? false;
|
||||
}
|
||||
|
||||
public async open<N extends WindowNames>(windowName: N, meta?: WindowMeta[N], config?: IWindowOpenConfig<N>): Promise<undefined>;
|
||||
|
|
@ -165,11 +165,11 @@ export class Window implements IWindowService {
|
|||
|
||||
// create new window
|
||||
const preferenceService = container.get<IPreferenceService>(serviceIdentifier.Preference);
|
||||
const { hideMenuBar: autoHideMenuBar, titleBar: showTitleBar, menuBarAlwaysOnTop, alwaysOnTop } = preferenceService.getPreferences();
|
||||
const { hideMenuBar: autoHideMenuBar, titleBar: showTitleBar, tidgiMiniWindowAlwaysOnTop, alwaysOnTop } = preferenceService.getPreferences();
|
||||
let windowWithBrowserViewConfig: Partial<BrowserWindowConstructorOptions> = {};
|
||||
let windowWithBrowserViewState: windowStateKeeperState | undefined;
|
||||
const WindowToKeepPositionState = [WindowNames.main, WindowNames.menuBar];
|
||||
const WindowWithBrowserView = [WindowNames.main, WindowNames.menuBar, WindowNames.secondary];
|
||||
const WindowToKeepPositionState = [WindowNames.main, WindowNames.tidgiMiniWindow];
|
||||
const WindowWithBrowserView = [WindowNames.main, WindowNames.tidgiMiniWindow, WindowNames.secondary];
|
||||
const isWindowWithBrowserView = WindowWithBrowserView.includes(windowName);
|
||||
if (WindowToKeepPositionState.includes(windowName)) {
|
||||
windowWithBrowserViewState = windowStateKeeper({
|
||||
|
|
@ -186,7 +186,7 @@ export class Window implements IWindowService {
|
|||
};
|
||||
}
|
||||
// hide titleBar should not take effect on setting window
|
||||
const hideTitleBar = [WindowNames.main, WindowNames.menuBar].includes(windowName) && !showTitleBar;
|
||||
const hideTitleBar = [WindowNames.main, WindowNames.tidgiMiniWindow].includes(windowName) && !showTitleBar;
|
||||
const windowConfig: BrowserWindowConstructorOptions = {
|
||||
...windowDimension[windowName],
|
||||
...windowWithBrowserViewConfig,
|
||||
|
|
@ -198,7 +198,7 @@ export class Window implements IWindowService {
|
|||
titleBarStyle: hideTitleBar ? 'hidden' : 'default',
|
||||
// https://www.electronjs.org/docs/latest/tutorial/custom-title-bar#add-native-window-controls-windows-linux
|
||||
...(hideTitleBar && process.platform !== 'darwin' ? { titleBarOverlay: true } : {}),
|
||||
alwaysOnTop: windowName === WindowNames.menuBar ? menuBarAlwaysOnTop : alwaysOnTop,
|
||||
alwaysOnTop: windowName === WindowNames.tidgiMiniWindow ? tidgiMiniWindowAlwaysOnTop : alwaysOnTop,
|
||||
webPreferences: {
|
||||
devTools: !isTest,
|
||||
nodeIntegration: false,
|
||||
|
|
@ -215,12 +215,12 @@ export class Window implements IWindowService {
|
|||
parent: isWindowWithBrowserView ? undefined : this.get(WindowNames.main),
|
||||
};
|
||||
let newWindow: BrowserWindow;
|
||||
if (windowName === WindowNames.menuBar) {
|
||||
this.mainWindowMenuBar = await handleAttachToMenuBar(windowConfig, windowWithBrowserViewState);
|
||||
if (this.mainWindowMenuBar.window === undefined) {
|
||||
throw new Error('MenuBar failed to create window.');
|
||||
if (windowName === WindowNames.tidgiMiniWindow) {
|
||||
this.tidgiMiniWindowMenubar = await handleAttachToTidgiMiniWindow(windowConfig, windowWithBrowserViewState);
|
||||
if (this.tidgiMiniWindowMenubar.window === undefined) {
|
||||
throw new Error('TidgiMiniWindow failed to create window.');
|
||||
}
|
||||
newWindow = this.mainWindowMenuBar.window;
|
||||
newWindow = this.tidgiMiniWindowMenubar.window;
|
||||
} else {
|
||||
newWindow = await handleCreateBasicWindow(windowName, windowConfig, meta, config);
|
||||
if (isWindowWithBrowserView) {
|
||||
|
|
@ -339,114 +339,114 @@ export class Window implements IWindowService {
|
|||
}
|
||||
}
|
||||
|
||||
public async toggleMenubarWindow(): Promise<void> {
|
||||
logger.info('toggleMenubarWindow called', { function: 'toggleMenubarWindow' });
|
||||
public async toggleTidgiMiniWindow(): Promise<void> {
|
||||
logger.info('toggleTidgiMiniWindow called', { function: 'toggleTidgiMiniWindow' });
|
||||
try {
|
||||
const preferenceService = container.get<IPreferenceService>(serviceIdentifier.Preference);
|
||||
|
||||
const isOpen = await this.isMenubarOpen();
|
||||
logger.debug('Menubar open status checked', { function: 'toggleMenubarWindow', isOpen });
|
||||
const isOpen = await this.isTidgiMiniWindowOpen();
|
||||
logger.debug('TidgiMiniWindow open status checked', { function: 'toggleTidgiMiniWindow', isOpen });
|
||||
if (isOpen) {
|
||||
logger.info('Closing menubar window', { function: 'toggleMenubarWindow' });
|
||||
await this.closeMenubarWindow();
|
||||
logger.info('Closing tidgi mini window', { function: 'toggleTidgiMiniWindow' });
|
||||
await this.closeTidgiMiniWindow();
|
||||
} else {
|
||||
const attachToMenubar = await preferenceService.get('attachToMenubar');
|
||||
logger.debug('attachToMenubar preference checked', { function: 'toggleMenubarWindow', attachToMenubar });
|
||||
if (attachToMenubar) {
|
||||
logger.info('Opening menubar window', { function: 'toggleMenubarWindow' });
|
||||
await this.openMenubarWindow(true, true); // Explicitly show window when toggling
|
||||
const attachToTidgiMiniWindow = await preferenceService.get('attachToTidgiMiniWindow');
|
||||
logger.debug('attachToTidgiMiniWindow preference checked', { function: 'toggleTidgiMiniWindow', attachToTidgiMiniWindow });
|
||||
if (attachToTidgiMiniWindow) {
|
||||
logger.info('Opening tidgi mini window', { function: 'toggleTidgiMiniWindow' });
|
||||
await this.openTidgiMiniWindow(true, true); // Explicitly show window when toggling
|
||||
} else {
|
||||
logger.warn('Cannot open menubar window: attachToMenubar preference is disabled', { function: 'toggleMenubarWindow' });
|
||||
logger.warn('Cannot open tidgi mini window: attachToTidgiMiniWindow preference is disabled', { function: 'toggleTidgiMiniWindow' });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to open/hide menubar window', { error });
|
||||
logger.error('Failed to open/hide tidgi mini window', { error });
|
||||
}
|
||||
}
|
||||
|
||||
public async openMenubarWindow(enableIt = true, showWindow = true): Promise<void> {
|
||||
public async openTidgiMiniWindow(enableIt = true, showWindow = true): Promise<void> {
|
||||
try {
|
||||
// Check if menubar is already enabled
|
||||
if (this.mainWindowMenuBar?.window !== undefined) {
|
||||
logger.debug('Menubar is already enabled, bring it to front', { function: 'openMenubarWindow' });
|
||||
// Check if tidgi mini window is already enabled
|
||||
if (this.tidgiMiniWindowMenubar?.window !== undefined) {
|
||||
logger.debug('TidGi mini window is already enabled, bring it to front', { function: 'openTidgiMiniWindow' });
|
||||
if (showWindow) {
|
||||
// Before showing, get the target workspace
|
||||
const [menubarSyncWorkspaceWithMainWindow, menubarFixedWorkspaceId] = await Promise.all([
|
||||
this.preferenceService.get('menubarSyncWorkspaceWithMainWindow'),
|
||||
this.preferenceService.get('menubarFixedWorkspaceId'),
|
||||
const [tidgiMiniWindowSyncWorkspaceWithMainWindow, tidgiMiniWindowFixedWorkspaceId] = await Promise.all([
|
||||
this.preferenceService.get('tidgiMiniWindowSyncWorkspaceWithMainWindow'),
|
||||
this.preferenceService.get('tidgiMiniWindowFixedWorkspaceId'),
|
||||
]);
|
||||
const shouldSync = menubarSyncWorkspaceWithMainWindow === undefined || menubarSyncWorkspaceWithMainWindow;
|
||||
const shouldSync = tidgiMiniWindowSyncWorkspaceWithMainWindow === undefined || tidgiMiniWindowSyncWorkspaceWithMainWindow;
|
||||
const targetWorkspaceId = shouldSync
|
||||
? (await container.get<IWorkspaceService>(serviceIdentifier.Workspace).getActiveWorkspace())?.id
|
||||
: menubarFixedWorkspaceId;
|
||||
: tidgiMiniWindowFixedWorkspaceId;
|
||||
|
||||
logger.info('openMenubarWindow: preparing to show window', {
|
||||
function: 'openMenubarWindow',
|
||||
logger.info('openTidgiMiniWindow: preparing to show window', {
|
||||
function: 'openTidgiMiniWindow',
|
||||
shouldSync,
|
||||
targetWorkspaceId,
|
||||
menubarSyncWorkspaceWithMainWindow,
|
||||
menubarFixedWorkspaceId,
|
||||
tidgiMiniWindowSyncWorkspaceWithMainWindow,
|
||||
tidgiMiniWindowFixedWorkspaceId,
|
||||
});
|
||||
|
||||
// Ensure view exists for the target workspace before realigning
|
||||
if (targetWorkspaceId) {
|
||||
const targetWorkspace = await container.get<IWorkspaceService>(serviceIdentifier.Workspace).get(targetWorkspaceId);
|
||||
if (targetWorkspace && !targetWorkspace.pageType) {
|
||||
// This is a wiki workspace - ensure it has a view for menubar window
|
||||
// This is a wiki workspace - ensure it has a view for tidgi mini window
|
||||
const viewService = container.get<IViewService>(serviceIdentifier.View);
|
||||
const existingView = viewService.getView(targetWorkspace.id, WindowNames.menuBar);
|
||||
const existingView = viewService.getView(targetWorkspace.id, WindowNames.tidgiMiniWindow);
|
||||
if (!existingView) {
|
||||
logger.info('openMenubarWindow: creating missing menubar view', {
|
||||
function: 'openMenubarWindow',
|
||||
logger.info('openTidgiMiniWindow: creating missing tidgi mini window view', {
|
||||
function: 'openTidgiMiniWindow',
|
||||
workspaceId: targetWorkspace.id,
|
||||
});
|
||||
await viewService.addView(targetWorkspace, WindowNames.menuBar);
|
||||
await viewService.addView(targetWorkspace, WindowNames.tidgiMiniWindow);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info('openMenubarWindow: calling realignActiveWorkspace', {
|
||||
function: 'openMenubarWindow',
|
||||
logger.info('openTidgiMiniWindow: calling realignActiveWorkspace', {
|
||||
function: 'openTidgiMiniWindow',
|
||||
targetWorkspaceId,
|
||||
});
|
||||
await container.get<IWorkspaceViewService>(serviceIdentifier.WorkspaceView).realignActiveWorkspace(targetWorkspaceId);
|
||||
logger.info('openMenubarWindow: realignActiveWorkspace completed', {
|
||||
function: 'openMenubarWindow',
|
||||
logger.info('openTidgiMiniWindow: realignActiveWorkspace completed', {
|
||||
function: 'openTidgiMiniWindow',
|
||||
targetWorkspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
// Use menuBar.showWindow() instead of direct window.show() for proper menubar behavior
|
||||
void this.mainWindowMenuBar.showWindow();
|
||||
// Use menuBar.showWindow() instead of direct window.show() for proper tidgi mini window behavior
|
||||
void this.tidgiMiniWindowMenubar.showWindow();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Create menubar window (create and open when enableIt is true)
|
||||
await this.open(WindowNames.menuBar);
|
||||
// Create tidgi mini window (create and open when enableIt is true)
|
||||
await this.open(WindowNames.tidgiMiniWindow);
|
||||
if (enableIt) {
|
||||
logger.debug('Menubar enabled', { function: 'openMenubarWindow' });
|
||||
// After creating the menubar, show it if requested
|
||||
if (showWindow && this.mainWindowMenuBar) {
|
||||
logger.debug('Showing newly created menubar window', { function: 'openMenubarWindow' });
|
||||
void this.mainWindowMenuBar.showWindow();
|
||||
logger.debug('TidGi mini window enabled', { function: 'openTidgiMiniWindow' });
|
||||
// After creating the tidgi mini window, show it if requested
|
||||
if (showWindow && this.tidgiMiniWindowMenubar) {
|
||||
logger.debug('Showing newly created tidgi mini window', { function: 'openTidgiMiniWindow' });
|
||||
void this.tidgiMiniWindowMenubar.showWindow();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to open menubar', { error, function: 'openMenubarWindow' });
|
||||
logger.error('Failed to open tidgi mini window', { error, function: 'openTidgiMiniWindow' });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async closeMenubarWindow(disableIt = false): Promise<void> {
|
||||
public async closeTidgiMiniWindow(disableIt = false): Promise<void> {
|
||||
try {
|
||||
// Check if menubar exists
|
||||
if (this.mainWindowMenuBar === undefined) {
|
||||
logger.debug('Menubar is already disabled', { function: 'closeMenubarWindow' });
|
||||
// Check if tidgi mini window exists
|
||||
if (this.tidgiMiniWindowMenubar === undefined) {
|
||||
logger.debug('TidGi mini window is already disabled', { function: 'closeTidgiMiniWindow' });
|
||||
return;
|
||||
}
|
||||
const menuBar = this.mainWindowMenuBar;
|
||||
const menuBar = this.tidgiMiniWindowMenubar;
|
||||
if (disableIt) {
|
||||
// Fully destroy menubar: destroy window and tray, then clear reference
|
||||
// Fully destroy tidgi mini window: destroy window and tray, then clear reference
|
||||
if (menuBar.window) {
|
||||
// remove custom close listener so destroy will actually close
|
||||
menuBar.window.removeAllListeners('close');
|
||||
|
|
@ -457,16 +457,16 @@ export class Window implements IWindowService {
|
|||
if (menuBar.tray && !menuBar.tray.isDestroyed()) {
|
||||
menuBar.tray.destroy();
|
||||
}
|
||||
this.mainWindowMenuBar = undefined;
|
||||
logger.debug('Menubar disabled successfully without restart', { function: 'closeMenubarWindow' });
|
||||
this.tidgiMiniWindowMenubar = undefined;
|
||||
logger.debug('TidGi mini window disabled successfully without restart', { function: 'closeTidgiMiniWindow' });
|
||||
} else {
|
||||
// Only hide the menubar window (keep tray and instance for re-open)
|
||||
// Use menuBar.hideWindow() for proper menubar behavior
|
||||
// Only hide the tidgi mini window (keep tray and instance for re-open)
|
||||
// Use menuBar.hideWindow() for proper tidgi mini window behavior
|
||||
menuBar.hideWindow();
|
||||
logger.debug('Menubar closed (kept enabled)', { function: 'closeMenubarWindow' });
|
||||
logger.debug('TidGi mini window closed (kept enabled)', { function: 'closeTidgiMiniWindow' });
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to close menubar', { error });
|
||||
logger.error('Failed to close tidgi mini window', { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
|
@ -484,10 +484,10 @@ export class Window implements IWindowService {
|
|||
logger.info(`Updated ${windowName} alwaysOnTop to ${properties.alwaysOnTop}`);
|
||||
}
|
||||
|
||||
// Handle menubar specific properties
|
||||
if (windowName === WindowNames.menuBar && this.mainWindowMenuBar?.window) {
|
||||
// Handle tidgi mini window specific properties
|
||||
if (windowName === WindowNames.tidgiMiniWindow && this.tidgiMiniWindowMenubar?.window) {
|
||||
if (properties.alwaysOnTop !== undefined) {
|
||||
this.mainWindowMenuBar.window.setAlwaysOnTop(properties.alwaysOnTop);
|
||||
this.tidgiMiniWindowMenubar.window.setAlwaysOnTop(properties.alwaysOnTop);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -498,79 +498,79 @@ export class Window implements IWindowService {
|
|||
|
||||
public async reactWhenPreferencesChanged(key: string, value: unknown): Promise<void> {
|
||||
switch (key) {
|
||||
case 'attachToMenubar': {
|
||||
case 'attachToTidgiMiniWindow': {
|
||||
if (value) {
|
||||
// Enable menubar without showing the window; visibility controlled by toggle/shortcut
|
||||
await this.openMenubarWindow(true, false);
|
||||
// Enable tidgi mini window without showing the window; visibility controlled by toggle/shortcut
|
||||
await this.openTidgiMiniWindow(true, false);
|
||||
|
||||
// After enabling menubar, create view for the current active workspace (if it's a wiki workspace)
|
||||
// After enabling tidgi mini window, create view for the current active workspace (if it's a wiki workspace)
|
||||
const workspaceService = container.get<IWorkspaceService>(serviceIdentifier.Workspace);
|
||||
const viewService = container.get<IViewService>(serviceIdentifier.View);
|
||||
const activeWorkspace = await workspaceService.getActiveWorkspace();
|
||||
|
||||
if (activeWorkspace && !activeWorkspace.pageType) {
|
||||
// This is a wiki workspace - ensure it has a view for menubar window
|
||||
const existingView = viewService.getView(activeWorkspace.id, WindowNames.menuBar);
|
||||
// This is a wiki workspace - ensure it has a view for tidgi mini window
|
||||
const existingView = viewService.getView(activeWorkspace.id, WindowNames.tidgiMiniWindow);
|
||||
if (!existingView) {
|
||||
await viewService.addView(activeWorkspace, WindowNames.menuBar);
|
||||
await viewService.addView(activeWorkspace, WindowNames.tidgiMiniWindow);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await this.closeMenubarWindow(true);
|
||||
await this.closeTidgiMiniWindow(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 'menubarSyncWorkspaceWithMainWindow':
|
||||
case 'menubarFixedWorkspaceId': {
|
||||
case 'tidgiMiniWindowSyncWorkspaceWithMainWindow':
|
||||
case 'tidgiMiniWindowFixedWorkspaceId': {
|
||||
logger.info('Preference changed', { function: 'reactWhenPreferencesChanged', key, value: JSON.stringify(value) });
|
||||
// When menubar workspace settings change, hide all views and let the next window show trigger realignment
|
||||
const menuBarWindow = this.get(WindowNames.menuBar);
|
||||
if (menuBarWindow) {
|
||||
// When tidgi mini window workspace settings change, hide all views and let the next window show trigger realignment
|
||||
const tidgiMiniWindow = this.get(WindowNames.tidgiMiniWindow);
|
||||
if (tidgiMiniWindow) {
|
||||
const workspaceService = container.get<IWorkspaceService>(serviceIdentifier.Workspace);
|
||||
const viewService = container.get<IViewService>(serviceIdentifier.View);
|
||||
const allWorkspaces = await workspaceService.getWorkspacesAsList();
|
||||
logger.debug(`Hiding all menubar views (${allWorkspaces.length} workspaces)`, { function: 'reactWhenPreferencesChanged', key });
|
||||
logger.debug(`Hiding all tidgi mini window views (${allWorkspaces.length} workspaces)`, { function: 'reactWhenPreferencesChanged', key });
|
||||
// Hide all views - the correct view will be shown when window is next opened
|
||||
await Promise.all(
|
||||
allWorkspaces.map(async (workspace) => {
|
||||
const view = viewService.getView(workspace.id, WindowNames.menuBar);
|
||||
const view = viewService.getView(workspace.id, WindowNames.tidgiMiniWindow);
|
||||
if (view) {
|
||||
await viewService.hideView(menuBarWindow, WindowNames.menuBar, workspace.id);
|
||||
await viewService.hideView(tidgiMiniWindow, WindowNames.tidgiMiniWindow, workspace.id);
|
||||
}
|
||||
}),
|
||||
);
|
||||
// View creation is handled by openMenubarWindow when the window is shown
|
||||
// View creation is handled by openTidgiMiniWindow when the window is shown
|
||||
} else {
|
||||
logger.warn('menuBarWindow not found, skipping view management', { function: 'reactWhenPreferencesChanged', key });
|
||||
logger.warn('tidgiMiniWindow not found, skipping view management', { function: 'reactWhenPreferencesChanged', key });
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 'menuBarAlwaysOnTop': {
|
||||
await this.updateWindowProperties(WindowNames.menuBar, { alwaysOnTop: value as boolean });
|
||||
case 'tidgiMiniWindowAlwaysOnTop': {
|
||||
await this.updateWindowProperties(WindowNames.tidgiMiniWindow, { alwaysOnTop: value as boolean });
|
||||
return;
|
||||
}
|
||||
case 'showMenubarWindowTitleBar': {
|
||||
case 'showTidgiMiniWindowTitleBar': {
|
||||
// Title bar style requires recreating the window
|
||||
// We need to fully destroy and recreate the menubar window with new titleBar settings
|
||||
logger.info('showMenubarWindowTitleBar changed, recreating menubar window', {
|
||||
// We need to fully destroy and recreate the tidgi mini window with new titleBar settings
|
||||
logger.info('showTidgiMiniWindowTitleBar changed, recreating tidgi mini window', {
|
||||
function: 'reactWhenPreferencesChanged',
|
||||
newValue: value,
|
||||
});
|
||||
|
||||
const wasVisible = await this.isMenubarOpen();
|
||||
logger.debug('Current menubar visibility', {
|
||||
const wasVisible = await this.isTidgiMiniWindowOpen();
|
||||
logger.debug('Current tidgi mini window visibility', {
|
||||
function: 'reactWhenPreferencesChanged',
|
||||
wasVisible,
|
||||
});
|
||||
|
||||
// Fully destroy current menubar window (disableIt = true)
|
||||
await this.closeMenubarWindow(true);
|
||||
logger.debug('Menubar window destroyed', { function: 'reactWhenPreferencesChanged' });
|
||||
// Fully destroy current tidgi mini window (disableIt = true)
|
||||
await this.closeTidgiMiniWindow(true);
|
||||
logger.debug('Tidgi mini window destroyed', { function: 'reactWhenPreferencesChanged' });
|
||||
|
||||
// Reopen menubar window with new titleBar setting from updated preferences
|
||||
// Reopen tidgi mini window with new titleBar setting from updated preferences
|
||||
// enableIt = true to recreate, showWindow = wasVisible to restore visibility
|
||||
await this.openMenubarWindow(true, wasVisible);
|
||||
logger.info('Menubar window recreated with new titleBar setting', {
|
||||
await this.openTidgiMiniWindow(true, wasVisible);
|
||||
logger.info('Tidgi mini window recreated with new titleBar setting', {
|
||||
function: 'reactWhenPreferencesChanged',
|
||||
showWindow: wasVisible,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export interface IWindowService {
|
|||
*/
|
||||
hide(windowName: WindowNames): Promise<void>;
|
||||
isFullScreen(windowName?: WindowNames): Promise<boolean | undefined>;
|
||||
isMenubarOpen(): Promise<boolean>;
|
||||
isTidgiMiniWindowOpen(): Promise<boolean>;
|
||||
loadURL(windowName: WindowNames, newUrl?: string): Promise<void>;
|
||||
maximize(): Promise<void>;
|
||||
/**
|
||||
|
|
@ -55,15 +55,15 @@ export interface IWindowService {
|
|||
set(windowName: WindowNames, win: BrowserWindow | undefined): void;
|
||||
setWindowMeta<N extends WindowNames>(windowName: N, meta?: WindowMeta[N]): Promise<void>;
|
||||
stopFindInPage(close?: boolean, windowName?: WindowNames): Promise<void>;
|
||||
toggleMenubarWindow(): Promise<void>;
|
||||
toggleTidgiMiniWindow(): Promise<void>;
|
||||
updateWindowMeta<N extends WindowNames>(windowName: N, meta?: WindowMeta[N]): Promise<void>;
|
||||
/** Open menubar window without restart - hot reload. enableIt=true means fully enable and open. */
|
||||
openMenubarWindow(enableIt?: boolean, showWindow?: boolean): Promise<void>;
|
||||
/** Close menubar window. disableIt=true means fully disable and cleanup tray. */
|
||||
closeMenubarWindow(disableIt?: boolean): Promise<void>;
|
||||
/** Open tidgi mini window without restart - hot reload. enableIt=true means fully enable and open. */
|
||||
openTidgiMiniWindow(enableIt?: boolean, showWindow?: boolean): Promise<void>;
|
||||
/** Close tidgi mini window. disableIt=true means fully disable and cleanup tray. */
|
||||
closeTidgiMiniWindow(disableIt?: boolean): Promise<void>;
|
||||
/** Update window properties without restart - hot reload */
|
||||
updateWindowProperties(windowName: WindowNames, properties: { alwaysOnTop?: boolean }): Promise<void>;
|
||||
/** React to preference changes related to windows (menubar etc.) */
|
||||
/** React to preference changes related to windows (tidgi mini window etc.) */
|
||||
reactWhenPreferencesChanged(key: string, value: unknown): Promise<void>;
|
||||
}
|
||||
export const WindowServiceIPCDescriptor = {
|
||||
|
|
@ -78,7 +78,7 @@ export const WindowServiceIPCDescriptor = {
|
|||
goForward: ProxyPropertyType.Function,
|
||||
goHome: ProxyPropertyType.Function,
|
||||
isFullScreen: ProxyPropertyType.Function,
|
||||
isMenubarOpen: ProxyPropertyType.Function,
|
||||
isTidgiMiniWindowOpen: ProxyPropertyType.Function,
|
||||
loadURL: ProxyPropertyType.Function,
|
||||
maximize: ProxyPropertyType.Function,
|
||||
open: ProxyPropertyType.Function,
|
||||
|
|
@ -87,10 +87,10 @@ export const WindowServiceIPCDescriptor = {
|
|||
sendToAllWindows: ProxyPropertyType.Function,
|
||||
setWindowMeta: ProxyPropertyType.Function,
|
||||
stopFindInPage: ProxyPropertyType.Function,
|
||||
toggleMenubarWindow: ProxyPropertyType.Function,
|
||||
toggleTidgiMiniWindow: ProxyPropertyType.Function,
|
||||
updateWindowMeta: ProxyPropertyType.Function,
|
||||
openMenubarWindow: ProxyPropertyType.Function,
|
||||
closeMenubarWindow: ProxyPropertyType.Function,
|
||||
openTidgiMiniWindow: ProxyPropertyType.Function,
|
||||
closeTidgiMiniWindow: ProxyPropertyType.Function,
|
||||
updateWindowProperties: ProxyPropertyType.Function,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -123,9 +123,9 @@ export async function registerMenu(): Promise<void> {
|
|||
// if back is called in popup window
|
||||
// navigate in the popup window instead
|
||||
if (browserWindow !== undefined) {
|
||||
// TODO: test if we really can get this isPopup value, and it works for help page popup and menubar window
|
||||
// TODO: test if we really can get this isPopup value, and it works for help page popup and tidgi mini window
|
||||
// const { isPopup = false } = await getFromRenderer<IBrowserViewMetaData>(MetaDataChannel.getViewMetaData, browserWindow);
|
||||
// const windowName = isPopup ? WindowNames.menuBar : WindowNames.main
|
||||
// const windowName = isPopup ? WindowNames.tidgiMiniWindow : WindowNames.main
|
||||
|
||||
await windowService.goForward();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ export class WorkspaceView implements IWorkspaceViewService {
|
|||
logger.debug('Skip because alreadyHaveView');
|
||||
return;
|
||||
}
|
||||
// Create browserView, and if user want a menubar, we also create a new window for that
|
||||
// Create browserView, and if user want a tidgi mini window, we also create a new window for that
|
||||
await this.addViewForAllBrowserViews(workspace);
|
||||
if (isNew && options.from === WikiCreationMethod.Create) {
|
||||
const view = container.get<IViewService>(serviceIdentifier.View).getView(workspace.id, WindowNames.main);
|
||||
|
|
@ -205,39 +205,39 @@ export class WorkspaceView implements IWorkspaceViewService {
|
|||
public async addViewForAllBrowserViews(workspace: IWorkspace): Promise<void> {
|
||||
const mainTask = container.get<IViewService>(serviceIdentifier.View).addView(workspace, WindowNames.main);
|
||||
|
||||
// For menubar window, decide which workspace to show based on preferences
|
||||
const menubarTask = (async () => {
|
||||
const [attachToMenubar, menubarSyncWorkspaceWithMainWindow, menubarFixedWorkspaceId] = await Promise.all([
|
||||
this.preferenceService.get('attachToMenubar'),
|
||||
this.preferenceService.get('menubarSyncWorkspaceWithMainWindow'),
|
||||
this.preferenceService.get('menubarFixedWorkspaceId'),
|
||||
// For tidgi mini window, decide which workspace to show based on preferences
|
||||
const tidgiMiniWindowTask = (async () => {
|
||||
const [attachToTidgiMiniWindow, tidgiMiniWindowSyncWorkspaceWithMainWindow, tidgiMiniWindowFixedWorkspaceId] = await Promise.all([
|
||||
this.preferenceService.get('attachToTidgiMiniWindow'),
|
||||
this.preferenceService.get('tidgiMiniWindowSyncWorkspaceWithMainWindow'),
|
||||
this.preferenceService.get('tidgiMiniWindowFixedWorkspaceId'),
|
||||
]);
|
||||
|
||||
if (!attachToMenubar) {
|
||||
if (!attachToTidgiMiniWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If syncing with main window (undefined means default to true, or explicitly true), use the current workspace
|
||||
const shouldSync = menubarSyncWorkspaceWithMainWindow === undefined || menubarSyncWorkspaceWithMainWindow;
|
||||
const shouldSync = tidgiMiniWindowSyncWorkspaceWithMainWindow === undefined || tidgiMiniWindowSyncWorkspaceWithMainWindow;
|
||||
if (shouldSync) {
|
||||
// Don't add view for pageType workspaces (they don't have browser views)
|
||||
if (!workspace.pageType) {
|
||||
await container.get<IViewService>(serviceIdentifier.View).addView(workspace, WindowNames.menuBar);
|
||||
await container.get<IViewService>(serviceIdentifier.View).addView(workspace, WindowNames.tidgiMiniWindow);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If not syncing and a fixed workspace is set, only add view if this IS the fixed workspace
|
||||
if (menubarFixedWorkspaceId && workspace.id === menubarFixedWorkspaceId) {
|
||||
if (tidgiMiniWindowFixedWorkspaceId && workspace.id === tidgiMiniWindowFixedWorkspaceId) {
|
||||
// Don't add view for pageType workspaces (they don't have browser views)
|
||||
if (!workspace.pageType) {
|
||||
await container.get<IViewService>(serviceIdentifier.View).addView(workspace, WindowNames.menuBar);
|
||||
await container.get<IViewService>(serviceIdentifier.View).addView(workspace, WindowNames.tidgiMiniWindow);
|
||||
}
|
||||
}
|
||||
// If not syncing and no fixed workspace is set, don't add any view (user needs to select one)
|
||||
})();
|
||||
|
||||
await Promise.all([mainTask, menubarTask]);
|
||||
await Promise.all([mainTask, tidgiMiniWindowTask]);
|
||||
}
|
||||
|
||||
public async openWorkspaceWindowWithView(workspace: IWorkspace, configs?: { uri?: string }): Promise<void> {
|
||||
|
|
@ -484,7 +484,7 @@ export class WorkspaceView implements IWorkspaceViewService {
|
|||
await Promise.all(
|
||||
workspaces.map(async (workspace) => {
|
||||
await Promise.all(
|
||||
[WindowNames.main, WindowNames.menuBar].map(async (windowName) => {
|
||||
[WindowNames.main, WindowNames.tidgiMiniWindow].map(async (windowName) => {
|
||||
const view = container.get<IViewService>(serviceIdentifier.View).getView(workspace.id, windowName);
|
||||
if (view !== undefined) {
|
||||
await container.get<IViewService>(serviceIdentifier.View).loadUrlForView(workspace, view);
|
||||
|
|
@ -577,7 +577,7 @@ export class WorkspaceView implements IWorkspaceViewService {
|
|||
return;
|
||||
}
|
||||
const mainWindow = container.get<IWindowService>(serviceIdentifier.Window).get(WindowNames.main);
|
||||
const menuBarWindow = container.get<IWindowService>(serviceIdentifier.Window).get(WindowNames.menuBar);
|
||||
const tidgiMiniWindow = container.get<IWindowService>(serviceIdentifier.Window).get(WindowNames.tidgiMiniWindow);
|
||||
|
||||
logger.info(
|
||||
`realignActiveWorkspaceView: id ${workspaceToRealign?.id ?? 'undefined'}`,
|
||||
|
|
@ -586,7 +586,7 @@ export class WorkspaceView implements IWorkspaceViewService {
|
|||
logger.warn('realignActiveWorkspaceView: no active workspace');
|
||||
return;
|
||||
}
|
||||
if (mainWindow === undefined && menuBarWindow === undefined) {
|
||||
if (mainWindow === undefined && tidgiMiniWindow === undefined) {
|
||||
logger.warn('realignActiveWorkspaceView: no active window');
|
||||
return;
|
||||
}
|
||||
|
|
@ -597,44 +597,46 @@ export class WorkspaceView implements IWorkspaceViewService {
|
|||
tasks.push(container.get<IViewService>(serviceIdentifier.View).realignActiveView(mainWindow, workspaceToRealign.id, WindowNames.main));
|
||||
logger.debug(`realignActiveWorkspaceView: realign main window for ${workspaceToRealign.id}.`);
|
||||
}
|
||||
if (menuBarWindow === undefined) {
|
||||
logger.info(`realignActiveWorkspaceView: no menuBarBrowserViewWebContent, skip menu bar window for ${workspaceToRealign.id}.`);
|
||||
if (tidgiMiniWindow === undefined) {
|
||||
logger.info(`realignActiveWorkspaceView: no tidgiMiniWindowBrowserViewWebContent, skip tidgi mini window for ${workspaceToRealign.id}.`);
|
||||
} else {
|
||||
// For menubar window, decide which workspace to show based on preferences
|
||||
const [menubarSyncWorkspaceWithMainWindow, menubarFixedWorkspaceId] = await Promise.all([
|
||||
this.preferenceService.get('menubarSyncWorkspaceWithMainWindow'),
|
||||
this.preferenceService.get('menubarFixedWorkspaceId'),
|
||||
// For tidgi mini window, decide which workspace to show based on preferences
|
||||
const [tidgiMiniWindowSyncWorkspaceWithMainWindow, tidgiMiniWindowFixedWorkspaceId] = await Promise.all([
|
||||
this.preferenceService.get('tidgiMiniWindowSyncWorkspaceWithMainWindow'),
|
||||
this.preferenceService.get('tidgiMiniWindowFixedWorkspaceId'),
|
||||
]);
|
||||
// Default to sync (undefined or true), otherwise use fixed workspace ID (fallback to main if not set)
|
||||
const shouldSync = menubarSyncWorkspaceWithMainWindow === undefined || menubarSyncWorkspaceWithMainWindow;
|
||||
const menubarWorkspaceId = shouldSync ? workspaceToRealign.id : (menubarFixedWorkspaceId || workspaceToRealign.id);
|
||||
const shouldSync = tidgiMiniWindowSyncWorkspaceWithMainWindow === undefined || tidgiMiniWindowSyncWorkspaceWithMainWindow;
|
||||
const tidgiMiniWindowWorkspaceId = shouldSync ? workspaceToRealign.id : (tidgiMiniWindowFixedWorkspaceId || workspaceToRealign.id);
|
||||
|
||||
// Check if the target workspace is a pageType workspace (which doesn't have a view)
|
||||
let shouldShowMenubarView = true;
|
||||
const menubarTargetWorkspace = await container.get<IWorkspaceService>(serviceIdentifier.Workspace).get(menubarWorkspaceId);
|
||||
let shouldShowTidgiMiniWindowView = true;
|
||||
const tidgiMiniWindowTargetWorkspace = await container.get<IWorkspaceService>(serviceIdentifier.Workspace).get(tidgiMiniWindowWorkspaceId);
|
||||
|
||||
if (menubarTargetWorkspace?.pageType) {
|
||||
if (tidgiMiniWindowTargetWorkspace?.pageType) {
|
||||
logger.debug(
|
||||
`realignActiveWorkspaceView: skip menu bar window because workspace ${menubarWorkspaceId} is a page type workspace. Hiding all views.`,
|
||||
`realignActiveWorkspaceView: skip tidgi mini window because workspace ${tidgiMiniWindowWorkspaceId} is a page type workspace. Hiding all views.`,
|
||||
);
|
||||
shouldShowMenubarView = false; // Don't show any view for pageType workspaces
|
||||
// Hide all existing views in the menubar window
|
||||
shouldShowTidgiMiniWindowView = false; // Don't show any view for pageType workspaces
|
||||
// Hide all existing views in the tidgi mini window
|
||||
const allWorkspaces = await container.get<IWorkspaceService>(serviceIdentifier.Workspace).getWorkspacesAsList();
|
||||
await Promise.all(
|
||||
allWorkspaces.map(async (workspace) => {
|
||||
const view = container.get<IViewService>(serviceIdentifier.View).getView(workspace.id, WindowNames.menuBar);
|
||||
if (view) {
|
||||
await container.get<IViewService>(serviceIdentifier.View).hideView(menuBarWindow, WindowNames.menuBar, workspace.id);
|
||||
const view = container.get<IViewService>(serviceIdentifier.View).getView(workspace.id, WindowNames.tidgiMiniWindow);
|
||||
if (view !== undefined) {
|
||||
await container.get<IViewService>(serviceIdentifier.View).hideView(tidgiMiniWindow, WindowNames.tidgiMiniWindow, workspace.id);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldShowMenubarView) {
|
||||
logger.debug(
|
||||
`realignActiveWorkspaceView: realign menu bar window for ${menubarWorkspaceId} (main: ${workspaceToRealign.id}, sync: ${String(menubarSyncWorkspaceWithMainWindow)}).`,
|
||||
if (shouldShowTidgiMiniWindowView) {
|
||||
logger.info(
|
||||
`realignActiveWorkspaceView: realign tidgi mini window for ${tidgiMiniWindowWorkspaceId} (main: ${workspaceToRealign.id}, sync: ${
|
||||
String(tidgiMiniWindowSyncWorkspaceWithMainWindow)
|
||||
}).`,
|
||||
);
|
||||
tasks.push(container.get<IViewService>(serviceIdentifier.View).realignActiveView(menuBarWindow, menubarWorkspaceId, WindowNames.menuBar));
|
||||
tasks.push(container.get<IViewService>(serviceIdentifier.View).realignActiveView(tidgiMiniWindow, tidgiMiniWindowWorkspaceId, WindowNames.tidgiMiniWindow));
|
||||
}
|
||||
}
|
||||
await Promise.all(tasks);
|
||||
|
|
@ -642,7 +644,7 @@ export class WorkspaceView implements IWorkspaceViewService {
|
|||
|
||||
private async hideWorkspaceView(idToDeactivate: string): Promise<void> {
|
||||
const mainWindow = container.get<IWindowService>(serviceIdentifier.Window).get(WindowNames.main);
|
||||
const menuBarWindow = container.get<IWindowService>(serviceIdentifier.Window).get(WindowNames.menuBar);
|
||||
const tidgiMiniWindow = container.get<IWindowService>(serviceIdentifier.Window).get(WindowNames.tidgiMiniWindow);
|
||||
const tasks = [];
|
||||
if (mainWindow === undefined) {
|
||||
logger.warn(`hideWorkspaceView: no mainBrowserWindow, skip main window browserView.`);
|
||||
|
|
@ -650,26 +652,26 @@ export class WorkspaceView implements IWorkspaceViewService {
|
|||
logger.info(`hideWorkspaceView: hide main window browserView.`);
|
||||
tasks.push(container.get<IViewService>(serviceIdentifier.View).hideView(mainWindow, WindowNames.main, idToDeactivate));
|
||||
}
|
||||
if (menuBarWindow === undefined) {
|
||||
logger.debug(`hideWorkspaceView: no menuBarBrowserWindow, skip menu bar window browserView.`);
|
||||
if (tidgiMiniWindow === undefined) {
|
||||
logger.debug(`hideWorkspaceView: no tidgiMiniWindowBrowserWindow, skip tidgi mini window browserView.`);
|
||||
} else {
|
||||
// For menubar window, only hide if syncing with main window OR if this is the fixed workspace being deactivated
|
||||
const [menubarSyncWorkspaceWithMainWindow, menubarFixedWorkspaceId] = await Promise.all([
|
||||
this.preferenceService.get('menubarSyncWorkspaceWithMainWindow'),
|
||||
this.preferenceService.get('menubarFixedWorkspaceId'),
|
||||
// For tidgi mini window, only hide if syncing with main window OR if this is the fixed workspace being deactivated
|
||||
const [tidgiMiniWindowSyncWorkspaceWithMainWindow, tidgiMiniWindowFixedWorkspaceId] = await Promise.all([
|
||||
this.preferenceService.get('tidgiMiniWindowSyncWorkspaceWithMainWindow'),
|
||||
this.preferenceService.get('tidgiMiniWindowFixedWorkspaceId'),
|
||||
]);
|
||||
|
||||
// Default to sync (undefined or true)
|
||||
const shouldSync = menubarSyncWorkspaceWithMainWindow === undefined || menubarSyncWorkspaceWithMainWindow;
|
||||
const shouldSync = tidgiMiniWindowSyncWorkspaceWithMainWindow === undefined || tidgiMiniWindowSyncWorkspaceWithMainWindow;
|
||||
|
||||
// Only hide menubar view if:
|
||||
// Only hide tidgi mini window view if:
|
||||
// 1. Syncing with main window (should hide when main window hides)
|
||||
// 2. OR the workspace being hidden is the fixed workspace (rare case, but should be handled)
|
||||
if (shouldSync || idToDeactivate === menubarFixedWorkspaceId) {
|
||||
logger.info(`hideWorkspaceView: hide menu bar window browserView.`);
|
||||
tasks.push(container.get<IViewService>(serviceIdentifier.View).hideView(menuBarWindow, WindowNames.menuBar, idToDeactivate));
|
||||
if (shouldSync || idToDeactivate === tidgiMiniWindowFixedWorkspaceId) {
|
||||
logger.info(`hideWorkspaceView: hide tidgi mini window browserView.`);
|
||||
tasks.push(container.get<IViewService>(serviceIdentifier.View).hideView(tidgiMiniWindow, WindowNames.tidgiMiniWindow, idToDeactivate));
|
||||
} else {
|
||||
logger.debug(`hideWorkspaceView: skip hiding menu bar window browserView (fixed workspace: ${menubarFixedWorkspaceId || 'none'}).`);
|
||||
logger.debug(`hideWorkspaceView: skip hiding tidgi mini window browserView (fixed workspace: ${tidgiMiniWindowFixedWorkspaceId || 'none'}).`);
|
||||
}
|
||||
}
|
||||
await Promise.all(tasks);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { Search } from './sections/Search';
|
|||
import { Sync } from './sections/Sync';
|
||||
import { System } from './sections/System';
|
||||
import { TiddlyWiki } from './sections/TiddlyWiki';
|
||||
import { TidGiMenubarWindow } from './sections/TidGiMenubarWindow';
|
||||
import { TidGiMiniWindow } from './sections/TidGiMiniWindow';
|
||||
import { Updates } from './sections/Updates';
|
||||
import { SectionSideBar } from './SectionsSideBar';
|
||||
import { usePreferenceSections } from './useSections';
|
||||
|
|
@ -67,7 +67,7 @@ export default function Preferences(): React.JSX.Element {
|
|||
<Inner>
|
||||
<TiddlyWiki sections={sections} requestRestartCountDown={requestRestartCountDown} />
|
||||
<General sections={sections} requestRestartCountDown={requestRestartCountDown} />
|
||||
<TidGiMenubarWindow sections={sections} />
|
||||
<TidGiMiniWindow sections={sections} />
|
||||
<Sync sections={sections} requestRestartCountDown={requestRestartCountDown} />
|
||||
<ExternalAPI sections={sections} />
|
||||
<AIAgent sections={sections} />
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import { Paper, SectionTitle } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
|
||||
export function TidGiMenubarWindow(props: Partial<ISectionProps>): React.JSX.Element {
|
||||
export function TidGiMiniWindow(props: Partial<ISectionProps>): React.JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const preference = usePreferenceObservable();
|
||||
const platform = usePromiseValue(async () => await window.service.context.get('platform'));
|
||||
|
|
@ -21,31 +21,31 @@ export function TidGiMenubarWindow(props: Partial<ISectionProps>): React.JSX.Ele
|
|||
|
||||
return (
|
||||
<>
|
||||
<SectionTitle ref={props.sections?.menubar.ref}>{t('Menu.TidGiMenuBar')}</SectionTitle>
|
||||
<SectionTitle ref={props.sections?.tidgiMiniWindow.ref}>{t('Menu.TidGiMiniWindow')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
{/* Attach to taskbar/menubar settings */}
|
||||
{/* Attach to taskbar/system tray settings */}
|
||||
<ListItem
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.attachToMenubar}
|
||||
checked={preference.attachToTidgiMiniWindow}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('attachToMenubar', event.target.checked);
|
||||
await window.service.preference.set('attachToTidgiMiniWindow', event.target.checked);
|
||||
}}
|
||||
data-testid='attach-to-menubar-switch'
|
||||
data-testid='attach-to-tidgi-mini-window-switch'
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText
|
||||
primary={platform === 'win32' ? t('Preference.AttachToTaskbar') : t('Preference.AttachToMenuBar')}
|
||||
secondary={platform === 'linux' ? undefined : t('Preference.AttachToMenuBarTip')}
|
||||
primary={platform === 'win32' ? t('Preference.AttachToTaskbar') : t('Preference.AttachToTidgiMiniWindow')}
|
||||
secondary={platform === 'linux' ? undefined : t('Preference.AttachToTidgiMiniWindowTip')}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
{/* Other settings are only visible when attached to taskbar/menubar */}
|
||||
{preference.attachToMenubar && (
|
||||
{/* Other settings are only visible when attached to taskbar/system tray */}
|
||||
{preference.attachToTidgiMiniWindow && (
|
||||
<>
|
||||
{/* Sidebar display settings */}
|
||||
<ListItem
|
||||
|
|
@ -53,57 +53,57 @@ export function TidGiMenubarWindow(props: Partial<ISectionProps>): React.JSX.Ele
|
|||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.sidebarOnMenubar}
|
||||
checked={preference.sidebarOnTidgiMiniWindow}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('sidebarOnMenubar', event.target.checked);
|
||||
await window.service.preference.set('sidebarOnTidgiMiniWindow', event.target.checked);
|
||||
}}
|
||||
data-testid='sidebar-on-menubar-switch'
|
||||
data-testid='sidebar-on-tidgi-mini-window-switch'
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText
|
||||
primary={platform === 'win32' ? t('Preference.AttachToTaskbarShowSidebar') : t('Preference.AttachToMenuBarShowSidebar')}
|
||||
secondary={platform === 'linux' ? undefined : t('Preference.AttachToMenuBarShowSidebarTip')}
|
||||
primary={platform === 'win32' ? t('Preference.AttachToTaskbarShowSidebar') : t('Preference.AttachToTidgiMiniWindowShowSidebar')}
|
||||
secondary={platform === 'linux' ? undefined : t('Preference.AttachToTidgiMiniWindowShowSidebarTip')}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* Show title bar on menubar window */}
|
||||
{/* Show title bar on tidgi mini window */}
|
||||
<ListItem
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.showMenubarWindowTitleBar}
|
||||
checked={preference.showTidgiMiniWindowTitleBar}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('showMenubarWindowTitleBar', event.target.checked);
|
||||
await window.service.preference.set('showTidgiMiniWindowTitleBar', event.target.checked);
|
||||
}}
|
||||
data-testid='menubar-titlebar-switch'
|
||||
data-testid='tidgi-mini-window-titlebar-switch'
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText
|
||||
primary={t('Preference.ShowMenubarWindowTitleBar')}
|
||||
secondary={t('Preference.ShowMenubarWindowTitleBarDetail')}
|
||||
primary={t('Preference.ShowTidgiMiniWindowTitleBar')}
|
||||
secondary={t('Preference.ShowTidgiMiniWindowTitleBarDetail')}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
{/* Keep menubar window on top of other windows */}
|
||||
{/* Keep tidgi mini window on top of other windows */}
|
||||
<ListItem
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.menuBarAlwaysOnTop}
|
||||
checked={preference.tidgiMiniWindowAlwaysOnTop}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('menuBarAlwaysOnTop', event.target.checked);
|
||||
await window.service.preference.set('tidgiMiniWindowAlwaysOnTop', event.target.checked);
|
||||
}}
|
||||
data-testid='menubar-always-on-top-switch'
|
||||
data-testid='tidgi-mini-window-always-on-top-switch'
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText primary={t('Preference.MenubarAlwaysOnTop')} secondary={t('Preference.MenubarAlwaysOnTopDetail')} />
|
||||
<ListItemText primary={t('Preference.TidgiMiniWindowAlwaysOnTop')} secondary={t('Preference.TidgiMiniWindowAlwaysOnTopDetail')} />
|
||||
</ListItem>
|
||||
|
||||
<Divider />
|
||||
|
|
@ -114,32 +114,32 @@ export function TidGiMenubarWindow(props: Partial<ISectionProps>): React.JSX.Ele
|
|||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.menubarSyncWorkspaceWithMainWindow}
|
||||
checked={preference.tidgiMiniWindowSyncWorkspaceWithMainWindow}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('menubarSyncWorkspaceWithMainWindow', event.target.checked);
|
||||
await window.service.preference.set('tidgiMiniWindowSyncWorkspaceWithMainWindow', event.target.checked);
|
||||
}}
|
||||
data-testid='menubar-sync-workspace-switch'
|
||||
data-testid='tidgi-mini-window-sync-workspace-switch'
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText
|
||||
primary={t('Preference.MenubarSyncWorkspaceWithMainWindow')}
|
||||
secondary={t('Preference.MenubarSyncWorkspaceWithMainWindowDetail')}
|
||||
primary={t('Preference.TidgiMiniWindowSyncWorkspaceWithMainWindow')}
|
||||
secondary={t('Preference.TidgiMiniWindowSyncWorkspaceWithMainWindowDetail')}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
{/* Select fixed workspace for TidGi menubar window */}
|
||||
{!preference.menubarSyncWorkspaceWithMainWindow && (
|
||||
{/* Select fixed workspace for TidGi mini window */}
|
||||
{!preference.tidgiMiniWindowSyncWorkspaceWithMainWindow && (
|
||||
<Box sx={{ p: 2 }}>
|
||||
<FormControl fullWidth variant='outlined' sx={{ mt: 1 }}>
|
||||
<InputLabel>{t('Preference.MenubarFixedWorkspace')}</InputLabel>
|
||||
<InputLabel>{t('Preference.TidgiMiniWindowFixedWorkspace')}</InputLabel>
|
||||
<Select
|
||||
value={preference.menubarFixedWorkspaceId || ''}
|
||||
value={preference.tidgiMiniWindowFixedWorkspaceId || ''}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('menubarFixedWorkspaceId', event.target.value);
|
||||
await window.service.preference.set('tidgiMiniWindowFixedWorkspaceId', event.target.value);
|
||||
}}
|
||||
label={t('Preference.MenubarFixedWorkspace')}
|
||||
inputProps={{ 'data-testid': 'menubar-fixed-workspace-select' }}
|
||||
label={t('Preference.TidgiMiniWindowFixedWorkspace')}
|
||||
inputProps={{ 'data-testid': 'tidgi-mini-window-fixed-workspace-select' }}
|
||||
>
|
||||
<MenuItem value=''>{t('Preference.SelectWorkspace')}</MenuItem>
|
||||
{workspaces?.map((workspace) => (
|
||||
|
|
@ -152,23 +152,23 @@ export function TidGiMenubarWindow(props: Partial<ISectionProps>): React.JSX.Ele
|
|||
</Box>
|
||||
)}
|
||||
|
||||
{/* Set shortcut key to toggle TidGi menubar window */}
|
||||
{/* Set shortcut key to toggle TidGi mini window */}
|
||||
<Box sx={{ p: 2 }}>
|
||||
<KeyboardShortcutRegister
|
||||
label={t('Preference.MenubarShortcutKey')}
|
||||
value={preference.keyboardShortcuts?.['Window.toggleMenubarWindow'] || ''}
|
||||
label={t('Preference.TidgiMiniWindowShortcutKey')}
|
||||
value={preference.keyboardShortcuts?.['Window.toggleTidgiMiniWindow'] || ''}
|
||||
onChange={async (value) => {
|
||||
if (value && value.trim() !== '') {
|
||||
await window.service.native.registerKeyboardShortcut<IWindowService>('Window', 'toggleMenubarWindow', value);
|
||||
await window.service.native.registerKeyboardShortcut<IWindowService>('Window', 'toggleTidgiMiniWindow', value);
|
||||
} else {
|
||||
await window.service.native.unregisterKeyboardShortcut<IWindowService>('Window', 'toggleMenubarWindow');
|
||||
await window.service.native.unregisterKeyboardShortcut<IWindowService>('Window', 'toggleTidgiMiniWindow');
|
||||
}
|
||||
}}
|
||||
data-testid='menubar-shortcut-input'
|
||||
data-testid='tidgi-mini-window-shortcut-input'
|
||||
/>
|
||||
<Box sx={{ mt: 1 }}>
|
||||
<Typography variant='caption' color='textSecondary'>
|
||||
{t('Preference.MenubarShortcutKeyHelperText')}
|
||||
{t('Preference.TidgiMiniWindowShortcutKeyHelperText')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
|
@ -10,7 +10,7 @@ import { defaultPreferences } from '@services/preferences/defaultPreferences';
|
|||
import type { IPreferences } from '@services/preferences/interface';
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import type { IWorkspace } from '@services/workspaces/interface';
|
||||
import { TidGiMenubarWindow } from '../TidGiMenubarWindow';
|
||||
import { TidGiMiniWindow } from '../TidGiMiniWindow';
|
||||
|
||||
const TestWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
|
|
@ -87,14 +87,14 @@ const createMockPreference = (overrides: Partial<IPreferences> = {}): IPreferenc
|
|||
...overrides,
|
||||
});
|
||||
|
||||
describe('TidGiMenubarWindow Component', () => {
|
||||
describe('TidGiMiniWindow Component', () => {
|
||||
let preferenceSubject: BehaviorSubject<IPreferences | undefined>;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
preferenceSubject = new BehaviorSubject<IPreferences | undefined>(
|
||||
createMockPreference({ attachToMenubar: false }),
|
||||
createMockPreference({ attachToTidgiMiniWindow: false }),
|
||||
);
|
||||
|
||||
Object.defineProperty(window.observables.preference, 'preference$', {
|
||||
|
|
@ -136,7 +136,7 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
const renderComponent = async () => {
|
||||
const result = render(
|
||||
<TestWrapper>
|
||||
<TidGiMenubarWindow />
|
||||
<TidGiMiniWindow />
|
||||
</TestWrapper>,
|
||||
);
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
|
||||
// Ensure component is fully rendered
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Menu.TidGiMenuBar')).toBeInTheDocument();
|
||||
expect(screen.getByText('Menu.TidGiMiniWindow')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
return result;
|
||||
|
|
@ -166,7 +166,7 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
|
||||
const { unmount } = render(
|
||||
<TestWrapper>
|
||||
<TidGiMenubarWindow />
|
||||
<TidGiMiniWindow />
|
||||
</TestWrapper>,
|
||||
);
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
it('should render after loading with preferences', async () => {
|
||||
await renderComponent();
|
||||
|
||||
expect(screen.getByText('Menu.TidGiMenuBar')).toBeInTheDocument();
|
||||
expect(screen.getByText('Menu.TidGiMiniWindow')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should load platform information from backend on mount', async () => {
|
||||
|
|
@ -195,7 +195,7 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
expect(window.service.workspace.getWorkspacesAsList).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should display correct attach to menubar text for Windows', async () => {
|
||||
it('should display correct attach to tidgi mini window text for Windows', async () => {
|
||||
Object.defineProperty(window.service.context, 'get', {
|
||||
value: vi.fn().mockResolvedValue('win32'),
|
||||
writable: true,
|
||||
|
|
@ -206,7 +206,7 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
expect(screen.getByText('Preference.AttachToTaskbar')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display correct attach to menubar text for macOS', async () => {
|
||||
it('should display correct attach to tidgi mini window text for macOS', async () => {
|
||||
Object.defineProperty(window.service.context, 'get', {
|
||||
value: vi.fn().mockResolvedValue('darwin'),
|
||||
writable: true,
|
||||
|
|
@ -214,13 +214,13 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
|
||||
await renderComponent();
|
||||
|
||||
expect(screen.getByText('Preference.AttachToMenuBar')).toBeInTheDocument();
|
||||
expect(screen.getByText('Preference.AttachToTidgiMiniWindow')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Attach to menubar toggle', () => {
|
||||
it('should display attach to menubar switch with correct initial state', async () => {
|
||||
preferenceSubject.next(createMockPreference({ attachToMenubar: false }));
|
||||
describe('Attach to tidgi mini window toggle', () => {
|
||||
it('should display attach to tidgi mini window switch with correct initial state', async () => {
|
||||
preferenceSubject.next(createMockPreference({ attachToTidgiMiniWindow: false }));
|
||||
await renderComponent();
|
||||
|
||||
const switches = screen.getAllByRole('checkbox');
|
||||
|
|
@ -228,9 +228,9 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
expect(attachSwitch).not.toBeChecked();
|
||||
});
|
||||
|
||||
it('should call backend API when attach to menubar is toggled', async () => {
|
||||
it('should call backend API when attach to tidgi mini window is toggled', async () => {
|
||||
const user = userEvent.setup();
|
||||
preferenceSubject.next(createMockPreference({ attachToMenubar: false }));
|
||||
preferenceSubject.next(createMockPreference({ attachToTidgiMiniWindow: false }));
|
||||
await renderComponent();
|
||||
|
||||
const switches = screen.getAllByRole('checkbox');
|
||||
|
|
@ -239,13 +239,13 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
await user.click(attachSwitch);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('attachToMenubar', true);
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('attachToTidgiMiniWindow', true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should toggle attach to menubar setting', async () => {
|
||||
it('should toggle attach to tidgi mini window setting', async () => {
|
||||
const user = userEvent.setup();
|
||||
preferenceSubject.next(createMockPreference({ attachToMenubar: false }));
|
||||
preferenceSubject.next(createMockPreference({ attachToTidgiMiniWindow: false }));
|
||||
await renderComponent();
|
||||
|
||||
const switches = screen.getAllByRole('checkbox');
|
||||
|
|
@ -254,45 +254,45 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
await user.click(attachSwitch);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('attachToMenubar', true);
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('attachToTidgiMiniWindow', true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Conditional settings visibility', () => {
|
||||
it('should hide additional settings when attachToMenubar is false', async () => {
|
||||
preferenceSubject.next(createMockPreference({ attachToMenubar: false }));
|
||||
it('should hide additional settings when attachToTidgiMiniWindow is false', async () => {
|
||||
preferenceSubject.next(createMockPreference({ attachToTidgiMiniWindow: false }));
|
||||
await renderComponent();
|
||||
|
||||
expect(screen.queryByText('Preference.AttachToTaskbarShowSidebar')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Preference.AttachToMenuBarShowSidebar')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Preference.MenubarAlwaysOnTop')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Preference.MenubarSyncWorkspaceWithMainWindow')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Preference.AttachToTidgiMiniWindowShowSidebar')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Preference.TidgiMiniWindowAlwaysOnTop')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Preference.TidgiMiniWindowSyncWorkspaceWithMainWindow')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show additional settings when attachToMenubar is true', async () => {
|
||||
it('should show additional settings when attachToTidgiMiniWindow is true', async () => {
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
sidebarOnMenubar: false,
|
||||
menuBarAlwaysOnTop: false,
|
||||
menubarSyncWorkspaceWithMainWindow: true,
|
||||
attachToTidgiMiniWindow: true,
|
||||
sidebarOnTidgiMiniWindow: false,
|
||||
tidgiMiniWindowAlwaysOnTop: false,
|
||||
tidgiMiniWindowSyncWorkspaceWithMainWindow: true,
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
||||
expect(screen.getByText('Preference.AttachToTaskbarShowSidebar')).toBeInTheDocument();
|
||||
expect(screen.getByText('Preference.MenubarAlwaysOnTop')).toBeInTheDocument();
|
||||
expect(screen.getByText('Preference.MenubarSyncWorkspaceWithMainWindow')).toBeInTheDocument();
|
||||
expect(screen.getByText('Preference.TidgiMiniWindowAlwaysOnTop')).toBeInTheDocument();
|
||||
expect(screen.getByText('Preference.TidgiMiniWindowSyncWorkspaceWithMainWindow')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Sidebar on menubar toggle', () => {
|
||||
describe('Sidebar on tidgi mini window toggle', () => {
|
||||
it('should display sidebar toggle with correct initial state', async () => {
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
sidebarOnMenubar: false,
|
||||
attachToTidgiMiniWindow: true,
|
||||
sidebarOnTidgiMiniWindow: false,
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
|
@ -310,8 +310,8 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
const user = userEvent.setup();
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
sidebarOnMenubar: false,
|
||||
attachToTidgiMiniWindow: true,
|
||||
sidebarOnTidgiMiniWindow: false,
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
|
@ -322,7 +322,7 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
await user.click(sidebarSwitch);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('sidebarOnMenubar', true);
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('sidebarOnTidgiMiniWindow', true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -331,8 +331,8 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
it('should display always on top toggle with correct initial state', async () => {
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
menuBarAlwaysOnTop: false,
|
||||
attachToTidgiMiniWindow: true,
|
||||
tidgiMiniWindowAlwaysOnTop: false,
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
|
@ -340,7 +340,7 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
const switches = screen.getAllByRole('checkbox');
|
||||
const alwaysOnTopSwitch = switches.find((s) => {
|
||||
const label = s.closest('li')?.querySelector('.MuiListItemText-primary');
|
||||
return label?.textContent === 'Preference.MenubarAlwaysOnTop';
|
||||
return label?.textContent === 'Preference.TidgiMiniWindowAlwaysOnTop';
|
||||
});
|
||||
|
||||
expect(alwaysOnTopSwitch).not.toBeChecked();
|
||||
|
|
@ -350,19 +350,19 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
const user = userEvent.setup();
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
menuBarAlwaysOnTop: false,
|
||||
attachToTidgiMiniWindow: true,
|
||||
tidgiMiniWindowAlwaysOnTop: false,
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
||||
const switches = screen.getAllByRole('checkbox');
|
||||
const alwaysOnTopSwitch = switches[2];
|
||||
const alwaysOnTopSwitch = switches[3];
|
||||
|
||||
await user.click(alwaysOnTopSwitch);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('menuBarAlwaysOnTop', true);
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('tidgiMiniWindowAlwaysOnTop', true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -371,8 +371,8 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
it('should display workspace sync toggle with correct initial state', async () => {
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
menubarSyncWorkspaceWithMainWindow: true,
|
||||
attachToTidgiMiniWindow: true,
|
||||
tidgiMiniWindowSyncWorkspaceWithMainWindow: true,
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
|
@ -380,7 +380,7 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
const switches = screen.getAllByRole('checkbox');
|
||||
const syncSwitch = switches.find((s) => {
|
||||
const label = s.closest('li')?.querySelector('.MuiListItemText-primary');
|
||||
return label?.textContent === 'Preference.MenubarSyncWorkspaceWithMainWindow';
|
||||
return label?.textContent === 'Preference.TidgiMiniWindowSyncWorkspaceWithMainWindow';
|
||||
});
|
||||
|
||||
expect(syncSwitch).toBeChecked();
|
||||
|
|
@ -390,27 +390,27 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
const user = userEvent.setup();
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
menubarSyncWorkspaceWithMainWindow: true,
|
||||
attachToTidgiMiniWindow: true,
|
||||
tidgiMiniWindowSyncWorkspaceWithMainWindow: true,
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
||||
const switches = screen.getAllByRole('checkbox');
|
||||
const syncSwitch = switches[3];
|
||||
const syncSwitch = switches[4];
|
||||
|
||||
await user.click(syncSwitch);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('menubarSyncWorkspaceWithMainWindow', false);
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('tidgiMiniWindowSyncWorkspaceWithMainWindow', false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should hide fixed workspace selector when sync is enabled', async () => {
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
menubarSyncWorkspaceWithMainWindow: true,
|
||||
attachToTidgiMiniWindow: true,
|
||||
tidgiMiniWindowSyncWorkspaceWithMainWindow: true,
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
|
@ -421,8 +421,8 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
it('should show fixed workspace selector when sync is disabled', async () => {
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
menubarSyncWorkspaceWithMainWindow: false,
|
||||
attachToTidgiMiniWindow: true,
|
||||
tidgiMiniWindowSyncWorkspaceWithMainWindow: false,
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
|
@ -437,8 +437,8 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
const user = userEvent.setup();
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
menubarSyncWorkspaceWithMainWindow: false,
|
||||
attachToTidgiMiniWindow: true,
|
||||
tidgiMiniWindowSyncWorkspaceWithMainWindow: false,
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
|
@ -455,9 +455,9 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
it('should have workspace selector that can trigger API calls', async () => {
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
menubarSyncWorkspaceWithMainWindow: false,
|
||||
menubarFixedWorkspaceId: '',
|
||||
attachToTidgiMiniWindow: true,
|
||||
tidgiMiniWindowSyncWorkspaceWithMainWindow: false,
|
||||
tidgiMiniWindowFixedWorkspaceId: '',
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
|
@ -474,9 +474,9 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
it('should display currently selected workspace', async () => {
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
menubarSyncWorkspaceWithMainWindow: false,
|
||||
menubarFixedWorkspaceId: 'workspace-2',
|
||||
attachToTidgiMiniWindow: true,
|
||||
tidgiMiniWindowSyncWorkspaceWithMainWindow: false,
|
||||
tidgiMiniWindowFixedWorkspaceId: 'workspace-2',
|
||||
}),
|
||||
);
|
||||
const { container } = await renderComponent();
|
||||
|
|
@ -491,25 +491,25 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
});
|
||||
|
||||
describe('Keyboard shortcut registration', () => {
|
||||
it('should display keyboard shortcut button when menubar is attached', async () => {
|
||||
it('should display keyboard shortcut button when tidgi mini window is attached', async () => {
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
attachToTidgiMiniWindow: true,
|
||||
keyboardShortcuts: {},
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
||||
const shortcutButton = screen.getByRole('button', { name: /Preference\.MenubarShortcutKey/ });
|
||||
const shortcutButton = screen.getByRole('button', { name: /Preference\.TidgiMiniWindowShortcutKey/ });
|
||||
expect(shortcutButton).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display current keyboard shortcut value', async () => {
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
attachToTidgiMiniWindow: true,
|
||||
keyboardShortcuts: {
|
||||
'Window.toggleMenubarWindow': 'Ctrl+Shift+M',
|
||||
'Window.toggleTidgiMiniWindow': 'Ctrl+Shift+M',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
|
@ -522,19 +522,19 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
const user = userEvent.setup();
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
attachToTidgiMiniWindow: true,
|
||||
keyboardShortcuts: {},
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
||||
const shortcutButton = screen.getByRole('button', { name: /Preference\.MenubarShortcutKey/ });
|
||||
const shortcutButton = screen.getByRole('button', { name: /Preference\.TidgiMiniWindowShortcutKey/ });
|
||||
|
||||
const mockOnChange = vi.fn(async (value: string) => {
|
||||
if (value && value.trim() !== '') {
|
||||
await window.service.native.registerKeyboardShortcut('Window', 'toggleMenubarWindow', value);
|
||||
await window.service.native.registerKeyboardShortcut('Window', 'toggleTidgiMiniWindow', value);
|
||||
} else {
|
||||
await window.service.native.unregisterKeyboardShortcut('Window', 'toggleMenubarWindow');
|
||||
await window.service.native.unregisterKeyboardShortcut('Window', 'toggleTidgiMiniWindow');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -545,7 +545,7 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
await user.click(shortcutButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.service.native.registerKeyboardShortcut).toHaveBeenCalledWith('Window', 'toggleMenubarWindow', 'Ctrl+Shift+T');
|
||||
expect(window.service.native.registerKeyboardShortcut).toHaveBeenCalledWith('Window', 'toggleTidgiMiniWindow', 'Ctrl+Shift+T');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -553,21 +553,21 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
const user = userEvent.setup();
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
attachToTidgiMiniWindow: true,
|
||||
keyboardShortcuts: {
|
||||
'Window.toggleMenubarWindow': 'Ctrl+Shift+M',
|
||||
'Window.toggleTidgiMiniWindow': 'Ctrl+Shift+M',
|
||||
},
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
||||
const shortcutButton = screen.getByRole('button', { name: /Preference\.MenubarShortcutKey/ });
|
||||
const shortcutButton = screen.getByRole('button', { name: /Preference\.TidgiMiniWindowShortcutKey/ });
|
||||
|
||||
const mockOnChange = vi.fn(async (value: string) => {
|
||||
if (value && value.trim() !== '') {
|
||||
await window.service.native.registerKeyboardShortcut('Window', 'toggleMenubarWindow', value);
|
||||
await window.service.native.registerKeyboardShortcut('Window', 'toggleTidgiMiniWindow', value);
|
||||
} else {
|
||||
await window.service.native.unregisterKeyboardShortcut('Window', 'toggleMenubarWindow');
|
||||
await window.service.native.unregisterKeyboardShortcut('Window', 'toggleTidgiMiniWindow');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -578,30 +578,30 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
await user.click(shortcutButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.service.native.unregisterKeyboardShortcut).toHaveBeenCalledWith('Window', 'toggleMenubarWindow');
|
||||
expect(window.service.native.unregisterKeyboardShortcut).toHaveBeenCalledWith('Window', 'toggleTidgiMiniWindow');
|
||||
});
|
||||
});
|
||||
|
||||
it('should display helper text for keyboard shortcut', async () => {
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
attachToTidgiMiniWindow: true,
|
||||
keyboardShortcuts: {},
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
||||
expect(screen.getByText('Preference.MenubarShortcutKeyHelperText')).toBeInTheDocument();
|
||||
expect(screen.getByText('Preference.TidgiMiniWindowShortcutKeyHelperText')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Integration: Toggle sequence', () => {
|
||||
it('should show all settings when attachToMenubar is toggled on', async () => {
|
||||
it('should show all settings when attachToTidgiMiniWindow is toggled on', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
// Create a fresh subject for this test to avoid interference
|
||||
const toggleTestSubject = new BehaviorSubject<IPreferences | undefined>(
|
||||
createMockPreference({ attachToMenubar: false }),
|
||||
createMockPreference({ attachToTidgiMiniWindow: false }),
|
||||
);
|
||||
|
||||
Object.defineProperty(window.observables.preference, 'preference$', {
|
||||
|
|
@ -623,32 +623,32 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
|
||||
render(
|
||||
<TestWrapper>
|
||||
<TidGiMenubarWindow />
|
||||
<TidGiMiniWindow />
|
||||
</TestWrapper>,
|
||||
);
|
||||
|
||||
// Wait for component to fully load
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Menu.TidGiMenuBar')).toBeInTheDocument();
|
||||
expect(screen.getByText('Menu.TidGiMiniWindow')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Verify additional settings are hidden initially
|
||||
expect(screen.queryByText('Preference.MenubarAlwaysOnTop')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Preference.TidgiMiniWindowAlwaysOnTop')).not.toBeInTheDocument();
|
||||
|
||||
// Click the attach to menubar toggle
|
||||
// Click the attach to tidgi mini window toggle
|
||||
const switches = screen.getAllByRole('checkbox');
|
||||
const attachSwitch = switches[0];
|
||||
await user.click(attachSwitch);
|
||||
|
||||
// Wait for API call
|
||||
await waitFor(() => {
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('attachToMenubar', true);
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('attachToTidgiMiniWindow', true);
|
||||
});
|
||||
|
||||
// Now verify new elements appear (they should appear automatically after the state update)
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Preference.MenubarAlwaysOnTop')).toBeInTheDocument();
|
||||
expect(screen.getByText('Preference.MenubarSyncWorkspaceWithMainWindow')).toBeInTheDocument();
|
||||
expect(screen.getByText('Preference.TidgiMiniWindowAlwaysOnTop')).toBeInTheDocument();
|
||||
expect(screen.getByText('Preference.TidgiMiniWindowSyncWorkspaceWithMainWindow')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -656,10 +656,10 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
const user = userEvent.setup();
|
||||
preferenceSubject.next(
|
||||
createMockPreference({
|
||||
attachToMenubar: true,
|
||||
sidebarOnMenubar: false,
|
||||
menuBarAlwaysOnTop: false,
|
||||
menubarSyncWorkspaceWithMainWindow: true,
|
||||
attachToTidgiMiniWindow: true,
|
||||
sidebarOnTidgiMiniWindow: false,
|
||||
tidgiMiniWindowAlwaysOnTop: false,
|
||||
tidgiMiniWindowSyncWorkspaceWithMainWindow: true,
|
||||
}),
|
||||
);
|
||||
await renderComponent();
|
||||
|
|
@ -668,17 +668,17 @@ describe('TidGiMenubarWindow Component', () => {
|
|||
|
||||
await user.click(switches[1]);
|
||||
await waitFor(() => {
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('sidebarOnMenubar', true);
|
||||
});
|
||||
|
||||
await user.click(switches[2]);
|
||||
await waitFor(() => {
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('menuBarAlwaysOnTop', true);
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('sidebarOnTidgiMiniWindow', true);
|
||||
});
|
||||
|
||||
await user.click(switches[3]);
|
||||
await waitFor(() => {
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('menubarSyncWorkspaceWithMainWindow', false);
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('tidgiMiniWindowAlwaysOnTop', true);
|
||||
});
|
||||
|
||||
await user.click(switches[4]);
|
||||
await waitFor(() => {
|
||||
expect(window.service.preference.set).toHaveBeenCalledWith('tidgiMiniWindowSyncWorkspaceWithMainWindow', false);
|
||||
});
|
||||
|
||||
expect(window.service.preference.set).toHaveBeenCalledTimes(3);
|
||||
|
|
@ -51,8 +51,8 @@ export function usePreferenceSections<SectionTitleElement = HTMLSpanElement>():
|
|||
Icon: GitHubIcon,
|
||||
ref: useRef<SectionTitleElement>(null),
|
||||
},
|
||||
[PreferenceSections.menubar]: {
|
||||
text: t('Menu.TidGiMenuBar'),
|
||||
[PreferenceSections.tidgiMiniWindow]: {
|
||||
text: t('Menu.TidGiMiniWindow'),
|
||||
Icon: PhonelinkIcon,
|
||||
ref: useRef<SectionTitleElement>(null),
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue