mirror of
https://github.com/devon22/obsidian-gridexplorer.git
synced 2026-07-22 05:38:16 +00:00
2.4.7
This commit is contained in:
parent
9409cafcc5
commit
e00dbf2fae
9 changed files with 383 additions and 103 deletions
85
main.ts
85
main.ts
|
|
@ -243,6 +243,9 @@ export default class GridExplorerPlugin extends Plugin {
|
|||
// 只處理左鍵
|
||||
if (evt.button !== 0) return;
|
||||
|
||||
// 若點擊的是屬性面板中 tag pill 的刪除按鈕,直接跳過
|
||||
if ((evt.target as HTMLElement).closest('.multi-select-pill-remove-button')) return;
|
||||
|
||||
// 從觸發點往上找,看是否碰到 tag 連結
|
||||
const el = (evt.target as HTMLElement).closest(
|
||||
'a.tag, /* 預覽模式中的 #tag */\n' +
|
||||
|
|
@ -296,6 +299,88 @@ export default class GridExplorerPlugin extends Plugin {
|
|||
view.render(true); // resetScroll
|
||||
}
|
||||
}, true); // 用 capture,可在其他 listener 前先吃到
|
||||
|
||||
this.setupCanvasDropHandlers();
|
||||
}
|
||||
|
||||
private setupCanvasDropHandlers() {
|
||||
const setup = () => {
|
||||
this.app.workspace.getLeavesOfType('canvas').forEach(leaf => {
|
||||
const canvasView = leaf.view as any;
|
||||
if (canvasView.gridExplorerDropHandler) {
|
||||
return;
|
||||
}
|
||||
canvasView.gridExplorerDropHandler = true;
|
||||
|
||||
const canvasEl = canvasView.containerEl;
|
||||
|
||||
const dragoverHandler = (evt: DragEvent) => {
|
||||
// 只處理來自 Grid Explorer 的檔案拖曳,以顯示正確的游標
|
||||
if (evt.dataTransfer?.types.includes('application/obsidian-grid-explorer-files')) {
|
||||
evt.preventDefault();
|
||||
evt.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
};
|
||||
|
||||
const dropHandler = async (evt: DragEvent) => {
|
||||
// 只處理來自 Grid Explorer 的拖曳事件
|
||||
if (!evt.dataTransfer?.types.includes('application/obsidian-grid-explorer-files')) {
|
||||
return; // 如果不是,則不做任何事,讓事件繼續傳遞給 Canvas 的預設處理器
|
||||
}
|
||||
|
||||
// 來自 Grid Explorer 的拖曳,處理它並阻止預設行為
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
|
||||
let filePath: string | undefined;
|
||||
|
||||
const data = evt.dataTransfer.getData('application/obsidian-grid-explorer-files');
|
||||
try {
|
||||
const paths = JSON.parse(data);
|
||||
// 目前我們只支援單一檔案拖放
|
||||
if (Array.isArray(paths) && paths.length === 1) {
|
||||
filePath = paths[0];
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Grid Explorer: Failed to parse drop data from Grid Explorer.", e);
|
||||
}
|
||||
|
||||
// 如果無法取得檔案路徑,則中止操作
|
||||
if (!filePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tfile = this.app.vault.getAbstractFileByPath(filePath);
|
||||
if (!(tfile instanceof TFile)) {
|
||||
console.warn('Grid Explorer: Dropped item is not a TFile or could not be found.', filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
const canvas = canvasView.canvas;
|
||||
if (!canvas) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pos = canvas.posFromEvt(evt);
|
||||
|
||||
const newNode = canvas.createFileNode({
|
||||
file: tfile,
|
||||
pos: pos,
|
||||
size: { width: 400, height: 400 }, // 預設大小
|
||||
focus: true, // 建立後自動對焦
|
||||
});
|
||||
|
||||
canvas.addNode(newNode);
|
||||
await canvas.requestSave();
|
||||
};
|
||||
|
||||
this.registerDomEvent(canvasEl, 'dragover', dragoverHandler);
|
||||
this.registerDomEvent(canvasEl, 'drop', dropHandler, true); // 使用捕獲階段,以優先處理事件
|
||||
});
|
||||
};
|
||||
|
||||
this.registerEvent(this.app.workspace.on('layout-change', setup));
|
||||
setup(); // 首次執行設定
|
||||
}
|
||||
|
||||
async openNoteInRecentFiles(file: TFile) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "gridexplorer",
|
||||
"name": "GridExplorer",
|
||||
"version": "2.4.6",
|
||||
"version": "2.4.7",
|
||||
"minAppVersion": "1.1.0",
|
||||
"description": "Browse note files in a grid view.",
|
||||
"author": "Devon22",
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "gridexplorer",
|
||||
"version": "2.4.6",
|
||||
"version": "2.4.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "gridexplorer",
|
||||
"version": "2.4.6",
|
||||
"version": "2.4.7",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.6",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "gridexplorer",
|
||||
"version": "2.4.6",
|
||||
"version": "2.4.7",
|
||||
"description": "Browse note files in a grid view.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
127
src/GridView.ts
127
src/GridView.ts
|
|
@ -196,30 +196,8 @@ export class GridView extends ItemView {
|
|||
|
||||
// 添加新增筆記按鈕
|
||||
const newNoteButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('new_note') } });
|
||||
newNoteButton.addEventListener('click', async () => {
|
||||
let newFileName = `${t('untitled')}.md`;
|
||||
let newFilePath = !this.sourcePath || this.sourcePath === '/' ? newFileName : `${this.sourcePath}/${newFileName}`;
|
||||
|
||||
// 檢查檔案是否已存在,如果存在則遞增編號
|
||||
let counter = 1;
|
||||
while (this.app.vault.getAbstractFileByPath(newFilePath)) {
|
||||
newFileName = `${t('untitled')} ${counter}.md`;
|
||||
newFilePath = !this.sourcePath || this.sourcePath === '/' ? newFileName : `${this.sourcePath}/${newFileName}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
try {
|
||||
// 建立新筆記
|
||||
const newFile = await this.app.vault.create(newFilePath, '');
|
||||
// 開啟新筆記
|
||||
await this.app.workspace.getLeaf().openFile(newFile);
|
||||
} catch (error) {
|
||||
console.error('An error occurred while creating a new note:', error);
|
||||
}
|
||||
});
|
||||
setIcon(newNoteButton, 'square-pen');
|
||||
|
||||
newNoteButton.addEventListener('contextmenu', (event) => {
|
||||
newNoteButton.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
const menu = new Menu();
|
||||
// 新增筆記
|
||||
|
|
@ -274,6 +252,32 @@ export class GridView extends ItemView {
|
|||
}
|
||||
});
|
||||
});
|
||||
// 新增畫布
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('new_canvas'))
|
||||
.setIcon('layout-dashboard')
|
||||
.onClick(async () => {
|
||||
let newFileName = `${t('untitled')}.canvas`;
|
||||
let newFilePath = !this.sourcePath || this.sourcePath === '/' ? newFileName : `${this.sourcePath}/${newFileName}`;
|
||||
|
||||
// 檢查檔案是否已存在,如果存在則遞增編號
|
||||
let counter = 1;
|
||||
while (this.app.vault.getAbstractFileByPath(newFilePath)) {
|
||||
newFileName = `${t('untitled')} ${counter}.canvas`;
|
||||
newFilePath = !this.sourcePath || this.sourcePath === '/' ? newFileName : `${this.sourcePath}/${newFileName}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
try {
|
||||
// 建立新筆記
|
||||
const newFile = await this.app.vault.create(newFilePath, '');
|
||||
// 開啟新筆記
|
||||
await this.app.workspace.getLeaf().openFile(newFile);
|
||||
} catch (error) {
|
||||
console.error('An error occurred while creating a new canvas:', error);
|
||||
}
|
||||
});
|
||||
});
|
||||
menu.showAtMouseEvent(event);
|
||||
});
|
||||
|
||||
|
|
@ -1318,52 +1322,39 @@ export class GridView extends ItemView {
|
|||
const fileCache = this.app.metadataCache.getFileCache(file);
|
||||
let matchesTags = false;
|
||||
|
||||
if (fileCache && fileCache.tags && Array.isArray(fileCache.tags)) {
|
||||
// 檢查內文中的標籤
|
||||
const tags = fileCache.tags;
|
||||
matchesTags = tagTerms.every(tag =>
|
||||
tags.some((t: any) => t && t.tag && t.tag.toLowerCase() === '#' + tag)
|
||||
);
|
||||
}
|
||||
|
||||
// 如果前面的標籤檢查沒有匹配,檢查 frontmatter 中的標籤
|
||||
if (!matchesTags && fileCache && fileCache.frontmatter && fileCache.frontmatter.tags) {
|
||||
let frontmatterTags = fileCache.frontmatter.tags;
|
||||
if (typeof frontmatterTags === 'string') {
|
||||
// 如果是字串,分割成陣列
|
||||
const tagArray = frontmatterTags.split(/[,\s]+/).filter(t => t.trim() !== '');
|
||||
matchesTags = tagTerms.every(tag =>
|
||||
tagArray.some(t => {
|
||||
// 去除可能的 # 符號再比較
|
||||
const cleanTag = t.toLowerCase().replace("#", '');
|
||||
|
||||
// 直接比較
|
||||
if (cleanTag === tag) return true;
|
||||
|
||||
// 如果是空格分隔的多個標籤,分割並檢查
|
||||
const subTags = cleanTag.split(/\s+/).filter(st => st.trim() !== '');
|
||||
return subTags.some(st => st === tag);
|
||||
})
|
||||
);
|
||||
} else if (Array.isArray(frontmatterTags)) {
|
||||
// 如果是陣列,直接檢查
|
||||
matchesTags = tagTerms.every(tag =>
|
||||
frontmatterTags.some(t => {
|
||||
if (typeof t === 'string') {
|
||||
// 去除可能的 # 符號再比較
|
||||
const cleanTag = t.toLowerCase().replace("#", '');
|
||||
|
||||
// 直接比較
|
||||
if (cleanTag === tag) return true;
|
||||
|
||||
// 如果是空格分隔的多個標籤,分割並檢查
|
||||
const subTags = cleanTag.split(/\s+/).filter(st => st.trim() !== '');
|
||||
return subTags.some(st => st === tag);
|
||||
}
|
||||
return false;
|
||||
})
|
||||
);
|
||||
if (fileCache) {
|
||||
const collectedTags: string[] = [];
|
||||
|
||||
// 內文標籤
|
||||
if (Array.isArray(fileCache.tags)) {
|
||||
for (const t of fileCache.tags) {
|
||||
if (t && t.tag) {
|
||||
const clean = t.tag.toLowerCase().replace(/^#/, '');
|
||||
collectedTags.push(...clean.split(/\s+/).filter(st => st.trim() !== ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// frontmatter 標籤
|
||||
if (fileCache.frontmatter && fileCache.frontmatter.tags) {
|
||||
const fmTags = fileCache.frontmatter.tags;
|
||||
if (typeof fmTags === 'string') {
|
||||
collectedTags.push(
|
||||
...fmTags.split(/[,\s]+/)
|
||||
.map(t => t.toLowerCase().replace(/^#/, ''))
|
||||
.filter(t => t.trim() !== '')
|
||||
);
|
||||
} else if (Array.isArray(fmTags)) {
|
||||
for (const t of fmTags) {
|
||||
if (typeof t === 'string') {
|
||||
const clean = t.toLowerCase().replace(/^#/, '');
|
||||
collectedTags.push(...clean.split(/\s+/).filter(st => st.trim() !== ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matchesTags = tagTerms.every(tag => collectedTags.includes(tag));
|
||||
}
|
||||
|
||||
// 如果標籤匹配,且檔名或內容也匹配(如果有一般搜尋詞的話),則加入結果
|
||||
|
|
|
|||
|
|
@ -20,20 +20,33 @@ export class SearchModal extends Modal {
|
|||
// 創建搜尋輸入框容器
|
||||
const searchContainer = contentEl.createDiv('ge-search-container');
|
||||
|
||||
// 創建搜尋輸入框容器
|
||||
const searchInputWrapper = searchContainer.createDiv('ge-search-input-wrapper');
|
||||
|
||||
// 創建標籤顯示區域
|
||||
const tagDisplayArea = searchInputWrapper.createDiv('ge-search-tag-display-area');
|
||||
|
||||
// 創建搜尋輸入框
|
||||
const searchInput = searchContainer.createEl('input', {
|
||||
const searchInput = searchInputWrapper.createEl('input', {
|
||||
type: 'text',
|
||||
value: this.defaultQuery,
|
||||
placeholder: t('search_placeholder')
|
||||
placeholder: t('search_placeholder'),
|
||||
cls: 'ge-search-input'
|
||||
});
|
||||
|
||||
// 創建輸入框容器包裝層
|
||||
const inputContainer = searchInputWrapper.createDiv('ge-input-container');
|
||||
|
||||
// 將輸入框移動到容器中
|
||||
inputContainer.appendChild(searchInput);
|
||||
|
||||
// 創建清空按鈕
|
||||
const clearButton = searchContainer.createDiv('ge-search-clear-button'); //這裡不是用 ge-clear-button
|
||||
const clearButton = inputContainer.createDiv('ge-search-clear-button'); //這裡不是用 ge-clear-button
|
||||
clearButton.style.display = this.defaultQuery ? 'flex' : 'none';
|
||||
setIcon(clearButton, 'x');
|
||||
|
||||
// 建立標籤建議容器
|
||||
const tagSuggestionContainer = contentEl.createDiv('ge-tag-suggestions');
|
||||
const tagSuggestionContainer = contentEl.createDiv('ge-search-tag-suggestions');
|
||||
tagSuggestionContainer.style.display = 'none';
|
||||
|
||||
// 取得並快取所有標籤 (移除 # 前綴)
|
||||
|
|
@ -61,7 +74,7 @@ export class SearchModal extends Modal {
|
|||
|
||||
tagSuggestionContainer.empty();
|
||||
tagSuggestions.forEach((tag, idx) => {
|
||||
const item = tagSuggestionContainer.createDiv('ge-tag-suggestion-item');
|
||||
const item = tagSuggestionContainer.createDiv('ge-search-tag-suggestion-item');
|
||||
item.textContent = `#${tag}`;
|
||||
if (idx === selectedSuggestionIndex) item.addClass('is-selected');
|
||||
item.addEventListener('mousedown', (e) => {
|
||||
|
|
@ -74,23 +87,28 @@ export class SearchModal extends Modal {
|
|||
|
||||
const applySuggestion = (index: number) => {
|
||||
if (index < 0 || index >= tagSuggestions.length) return;
|
||||
const value = searchInput.value;
|
||||
const value = searchInput.value.trim();
|
||||
const cursor = searchInput.selectionStart || 0;
|
||||
const beforeMatch = value.substring(0, cursor).replace(/#([^#\\s]*)$/, `#${tagSuggestions[index]} `);
|
||||
const afterCursor = value.substring(cursor);
|
||||
searchInput.value = beforeMatch + afterCursor;
|
||||
searchInput.value = searchInput.value.trim();
|
||||
const newCursorPos = beforeMatch.length;
|
||||
searchInput.setSelectionRange(newCursorPos, newCursorPos);
|
||||
tagSuggestionContainer.style.display = 'none';
|
||||
tagSuggestionContainer.empty();
|
||||
selectedSuggestionIndex = -1;
|
||||
clearButton.style.display = searchInput.value ? 'flex' : 'none';
|
||||
|
||||
// 更新標籤按鈕顯示
|
||||
renderTagButtons();
|
||||
};
|
||||
|
||||
// 監聽輸入框變化來控制清空按鈕的顯示並更新標籤建議
|
||||
searchInput.addEventListener('input', () => {
|
||||
clearButton.style.display = searchInput.value ? 'flex' : 'none';
|
||||
updateTagSuggestions();
|
||||
renderTagButtons();
|
||||
});
|
||||
|
||||
// 處理上下鍵及 Enter 選擇建議
|
||||
|
|
@ -116,6 +134,8 @@ export class SearchModal extends Modal {
|
|||
clearButton.addEventListener('click', () => {
|
||||
searchInput.value = '';
|
||||
clearButton.style.display = 'none';
|
||||
tagDisplayArea.empty();
|
||||
tagDisplayArea.style.display = 'none';
|
||||
searchInput.focus();
|
||||
});
|
||||
|
||||
|
|
@ -192,6 +212,86 @@ export class SearchModal extends Modal {
|
|||
text: t('cancel')
|
||||
});
|
||||
|
||||
// 解析輸入內容並渲染成按鈕
|
||||
const renderTagButtons = () => {
|
||||
// 清空現有標籤顯示區域
|
||||
tagDisplayArea.empty();
|
||||
|
||||
// 獲取輸入值
|
||||
const inputValue = searchInput.value.trim();
|
||||
|
||||
// 如果輸入為空,隱藏標籤顯示區域
|
||||
if (!inputValue) {
|
||||
tagDisplayArea.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用空格分割輸入內容
|
||||
const terms = inputValue.split(/\s+/);
|
||||
|
||||
// 如果沒有分割出任何詞彙,隱藏標籤顯示區域
|
||||
if (terms.length === 0) {
|
||||
tagDisplayArea.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
// 顯示標籤顯示區域
|
||||
tagDisplayArea.style.display = 'flex';
|
||||
|
||||
// 分析輸入內容中各詞彙的位置
|
||||
let currentIndex = 0;
|
||||
const termPositions: {term: string, startIndex: number, endIndex: number}[] = [];
|
||||
|
||||
terms.forEach(term => {
|
||||
if (!term) return; // 跳過空詞彙
|
||||
|
||||
// 尋找該詞彙在原始輸入中的位置
|
||||
const startIndex = inputValue.indexOf(term, currentIndex);
|
||||
if (startIndex === -1) return; // 如果找不到,跳過
|
||||
|
||||
const endIndex = startIndex + term.length;
|
||||
|
||||
termPositions.push({
|
||||
term: term,
|
||||
startIndex: startIndex,
|
||||
endIndex: endIndex
|
||||
});
|
||||
|
||||
currentIndex = endIndex;
|
||||
});
|
||||
|
||||
// 為每個詞彙創建按鈕
|
||||
termPositions.forEach(termInfo => {
|
||||
const tagButton = tagDisplayArea.createDiv('ge-search-tag-button');
|
||||
tagButton.textContent = termInfo.term;
|
||||
|
||||
// 判斷是否為標籤,如果是則添加特殊樣式
|
||||
if (termInfo.term.startsWith('#')) {
|
||||
tagButton.addClass('is-tag');
|
||||
}
|
||||
|
||||
// 創建刪除按鈕
|
||||
const deleteButton = tagButton.createDiv('ge-search-tag-delete-button');
|
||||
setIcon(deleteButton, 'x');
|
||||
|
||||
// 點擊刪除按鈕時從輸入框中移除該詞彙
|
||||
deleteButton.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const newValue =
|
||||
inputValue.substring(0, termInfo.startIndex) +
|
||||
inputValue.substring(termInfo.endIndex);
|
||||
searchInput.value = newValue.trim();
|
||||
|
||||
// 觸發輸入事件以更新UI
|
||||
const inputEvent = new Event('input', { bubbles: true });
|
||||
searchInput.dispatchEvent(inputEvent);
|
||||
|
||||
// 聚焦回輸入框
|
||||
searchInput.focus();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 綁定搜尋事件
|
||||
const performSearch = () => {
|
||||
this.gridView.searchQuery = searchInput.value;
|
||||
|
|
@ -215,6 +315,9 @@ export class SearchModal extends Modal {
|
|||
this.close();
|
||||
});
|
||||
|
||||
// 初始渲染標籤按鈕
|
||||
renderTagButtons();
|
||||
|
||||
// 自動聚焦到搜尋輸入框,並將游標移到最後
|
||||
searchInput.focus();
|
||||
searchInput.setSelectionRange(searchInput.value.length, searchInput.value.length);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'cancel': '取消',
|
||||
'new_note': '新增筆記',
|
||||
'new_folder': '新增資料夾',
|
||||
'new_canvas': '新增畫布',
|
||||
'delete_folder': '刪除資料夾',
|
||||
'untitled': '未命名',
|
||||
'files': '個檔案',
|
||||
|
|
@ -250,6 +251,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'cancel': 'Cancel',
|
||||
'new_note': 'New note',
|
||||
'new_folder': 'New folder',
|
||||
'new_canvas': 'New canvas',
|
||||
'delete_folder': 'Delete folder',
|
||||
'untitled': 'Untitled',
|
||||
'files': 'files',
|
||||
|
|
@ -462,6 +464,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'cancel': '取消',
|
||||
'new_note': '新建笔记',
|
||||
'new_folder': '新建文件夹',
|
||||
'new_canvas': '新建画布',
|
||||
'delete_folder': '删除文件夹',
|
||||
'untitled': '未命名',
|
||||
'files': '个文件',
|
||||
|
|
@ -674,6 +677,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'cancel': 'キャンセル',
|
||||
'new_note': '新規ノート',
|
||||
'new_folder': '新規フォルダ',
|
||||
'new_canvas': '新規キャンバス',
|
||||
'delete_folder': '削除フォルダ',
|
||||
'untitled': '無題',
|
||||
'files': 'ファイル',
|
||||
|
|
@ -886,6 +890,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'cancel': 'Отмена',
|
||||
'new_note': 'Новая заметка',
|
||||
'new_folder': 'Новая папка',
|
||||
'new_canvas': 'Новый канвас',
|
||||
'delete_folder': 'Удалить папку',
|
||||
'untitled': 'Без названия',
|
||||
'files': 'файлы',
|
||||
|
|
@ -1098,6 +1103,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'cancel': 'Скасувати',
|
||||
'new_note': 'Нова нотатка',
|
||||
'new_folder': 'Нова папка',
|
||||
'new_canvas': 'Новий канвас',
|
||||
'delete_folder': 'Видалити папку',
|
||||
'untitled': 'Без назви',
|
||||
'files': 'файли',
|
||||
|
|
|
|||
143
styles.css
143
styles.css
|
|
@ -377,6 +377,125 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.ge-search-input-wrapper {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ge-input-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ge-search-clear-button {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.ge-search-clear-button:hover {
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
.ge-search-clear-button svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
/* 標籤顯示區域 */
|
||||
.ge-search-tag-display-area {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
padding: 5px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 4px;
|
||||
background-color: var(--background-primary);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ge-search-tag-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 3px 7px;
|
||||
background-color: var(--background-modifier-active-hover);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
position: relative;
|
||||
padding-right: 24px;
|
||||
height: 24px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.ge-search-tag-delete-button {
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.ge-search-tag-delete-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ge-search-tag-delete-button svg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
/* 標籤自動完成下拉選單 */
|
||||
.ge-search-tag-suggestions {
|
||||
position: relative;
|
||||
margin-top: 4px;
|
||||
background-color: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 4px;
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.ge-search-tag-suggestion-item {
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ge-search-tag-suggestion-item:hover,
|
||||
.ge-search-tag-suggestion-item.is-selected {
|
||||
background-color: var(--interactive-accent);
|
||||
color: var(--text-on-accent, white);
|
||||
}
|
||||
|
||||
.ge-search-container input {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
|
|
@ -947,27 +1066,3 @@
|
|||
width: 60px;
|
||||
}
|
||||
|
||||
/* 標籤自動完成下拉選單 */
|
||||
.ge-tag-suggestions {
|
||||
position: relative;
|
||||
margin-top: 4px;
|
||||
background-color: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 4px;
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.ge-tag-suggestion-item {
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ge-tag-suggestion-item:hover,
|
||||
.ge-tag-suggestion-item.is-selected {
|
||||
background-color: var(--interactive-accent);
|
||||
color: var(--text-on-accent, white);
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"2.4.6": "1.1.0"
|
||||
"2.4.7": "1.1.0"
|
||||
}
|
||||
Loading…
Reference in a new issue