mirror of
https://gitlab.com/ArkHost/HelixNotes.git
synced 2026-09-19 09:27:29 +02:00
Refine note info path and history
This commit is contained in:
@@ -1231,6 +1231,23 @@ pub fn save_vault_state(state: State<'_, AppState>, vault_state: VaultState) ->
|
|||||||
|
|
||||||
// ── Clipboard ──
|
// ── Clipboard ──
|
||||||
|
|
||||||
|
/// Copy text to the system clipboard through the native backend.
|
||||||
|
#[cfg(desktop)]
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn copy_text_to_clipboard(text: String) -> Result<(), String> {
|
||||||
|
let mut clipboard =
|
||||||
|
arboard::Clipboard::new().map_err(|e| format!("Clipboard init failed: {}", e))?;
|
||||||
|
clipboard
|
||||||
|
.set_text(text)
|
||||||
|
.map_err(|e| format!("Failed to copy text: {}", e))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(mobile)]
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn copy_text_to_clipboard(_text: String) -> Result<(), String> {
|
||||||
|
Err("Text clipboard copy is only supported on desktop".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
/// Read image from system clipboard (bypasses WebKitGTK clipboard bug).
|
/// Read image from system clipboard (bypasses WebKitGTK clipboard bug).
|
||||||
/// Returns PNG bytes as Vec<u8>, or error if no image on clipboard.
|
/// Returns PNG bytes as Vec<u8>, or error if no image on clipboard.
|
||||||
#[cfg(desktop)]
|
#[cfg(desktop)]
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ pub fn run() {
|
|||||||
commands::empty_trash,
|
commands::empty_trash,
|
||||||
commands::load_vault_state,
|
commands::load_vault_state,
|
||||||
commands::save_vault_state,
|
commands::save_vault_state,
|
||||||
|
commands::copy_text_to_clipboard,
|
||||||
commands::read_clipboard_image,
|
commands::read_clipboard_image,
|
||||||
commands::copy_image_to_clipboard,
|
commands::copy_image_to_clipboard,
|
||||||
commands::save_image,
|
commands::save_image,
|
||||||
|
|||||||
@@ -217,6 +217,10 @@ export async function saveVaultState(vaultState: VaultState): Promise<void> {
|
|||||||
return invoke("save_vault_state", { vaultState });
|
return invoke("save_vault_state", { vaultState });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function copyTextToClipboard(text: string): Promise<void> {
|
||||||
|
return invoke("copy_text_to_clipboard", { text });
|
||||||
|
}
|
||||||
|
|
||||||
export async function readClipboardImage(): Promise<number[]> {
|
export async function readClipboardImage(): Promise<number[]> {
|
||||||
return invoke("read_clipboard_image");
|
return invoke("read_clipboard_image");
|
||||||
}
|
}
|
||||||
|
|||||||
+113
-113
@@ -37,7 +37,7 @@
|
|||||||
import { convertFileSrc } from '@tauri-apps/api/core';
|
import { convertFileSrc } from '@tauri-apps/api/core';
|
||||||
import { getCurrentWindow } from '@tauri-apps/api/window';
|
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||||
import { readFile } from '@tauri-apps/plugin-fs';
|
import { readFile } from '@tauri-apps/plugin-fs';
|
||||||
import { openFile, openUrl, copyFileTo, copyImageToClipboard as copyImageToClipboardCmd, writeBytesTo, copyPngToClipboard } from '$lib/api';
|
import { openFile, openUrl, copyFileTo, copyImageToClipboard as copyImageToClipboardCmd, writeBytesTo, copyPngToClipboard, copyTextToClipboard } from '$lib/api';
|
||||||
import { save as saveDialog } from '@tauri-apps/plugin-dialog';
|
import { save as saveDialog } from '@tauri-apps/plugin-dialog';
|
||||||
import { activeNote, activeNotePath, appConfig, editorDirty, sourceMode, focusMode, readOnly, quickAccessPaths, notes, navHistory, canGoBack, canGoForward, viewerNote, notebooks, outlineWidth } from '$lib/stores/app';
|
import { activeNote, activeNotePath, appConfig, editorDirty, sourceMode, focusMode, readOnly, quickAccessPaths, notes, navHistory, canGoBack, canGoForward, viewerNote, notebooks, outlineWidth } from '$lib/stores/app';
|
||||||
import { saveNote, saveImage, saveAttachment, readClipboardImage, addQuickAccess, removeQuickAccess, getQuickAccess, getNoteVersions, getNoteVersionContent, createVersion, aiAsk, getAllNoteTitles, readNote, renameNote } from '$lib/api';
|
import { saveNote, saveImage, saveAttachment, readClipboardImage, addQuickAccess, removeQuickAccess, getQuickAccess, getNoteVersions, getNoteVersionContent, createVersion, aiAsk, getAllNoteTitles, readNote, renameNote } from '$lib/api';
|
||||||
@@ -331,6 +331,15 @@
|
|||||||
let infoToggleBtnEl = $state<HTMLElement | null>(null);
|
let infoToggleBtnEl = $state<HTMLElement | null>(null);
|
||||||
let wordCount = $state(0);
|
let wordCount = $state(0);
|
||||||
let charCount = $state(0);
|
let charCount = $state(0);
|
||||||
|
let infoPathCopyState = $state<'idle' | 'copied' | 'error'>('idle');
|
||||||
|
let infoPathCopyTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
$activeNotePath;
|
||||||
|
infoPathCopyState = 'idle';
|
||||||
|
if (infoPathCopyTimer) clearTimeout(infoPathCopyTimer);
|
||||||
|
infoPathCopyTimer = null;
|
||||||
|
});
|
||||||
|
|
||||||
// In-note search
|
// In-note search
|
||||||
let noteSearchOpen = $state(false);
|
let noteSearchOpen = $state(false);
|
||||||
@@ -3066,19 +3075,27 @@
|
|||||||
|
|
||||||
const scheduleCounts = debounce(updateCounts, 250);
|
const scheduleCounts = debounce(updateCounts, 250);
|
||||||
|
|
||||||
async function toggleInfo() {
|
function toggleInfo() {
|
||||||
if (!showInfo && $activeNote) {
|
|
||||||
historyLoading = true;
|
|
||||||
try {
|
|
||||||
historyVersions = await getNoteVersions($activeNote.meta.id);
|
|
||||||
} catch {
|
|
||||||
historyVersions = [];
|
|
||||||
}
|
|
||||||
historyLoading = false;
|
|
||||||
}
|
|
||||||
showInfo = !showInfo;
|
showInfo = !showInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function copyActiveNotePath() {
|
||||||
|
const path = $activeNotePath;
|
||||||
|
if (!path) return;
|
||||||
|
if (infoPathCopyTimer) clearTimeout(infoPathCopyTimer);
|
||||||
|
try {
|
||||||
|
await copyTextToClipboard(path);
|
||||||
|
infoPathCopyState = 'copied';
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to copy note path:', e);
|
||||||
|
infoPathCopyState = 'error';
|
||||||
|
}
|
||||||
|
infoPathCopyTimer = setTimeout(() => {
|
||||||
|
infoPathCopyState = 'idle';
|
||||||
|
infoPathCopyTimer = null;
|
||||||
|
}, 1600);
|
||||||
|
}
|
||||||
|
|
||||||
async function toggleHistory() {
|
async function toggleHistory() {
|
||||||
showHistory = !showHistory;
|
showHistory = !showHistory;
|
||||||
historyPreview = null;
|
historyPreview = null;
|
||||||
@@ -5806,6 +5823,7 @@
|
|||||||
clearTaskReveal();
|
clearTaskReveal();
|
||||||
destroyEditor();
|
destroyEditor();
|
||||||
unlistenFileChange?.();
|
unlistenFileChange?.();
|
||||||
|
if (infoPathCopyTimer) clearTimeout(infoPathCopyTimer);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -6401,37 +6419,35 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
{#if !isMobile && $activeNotePath}
|
||||||
|
<div class="info-note-path">
|
||||||
<div class="info-section info-section-versions">
|
<div class="info-note-path-header">
|
||||||
<div class="info-section-header">
|
<span class="info-key">Path on disk</span>
|
||||||
<div class="info-section-label">Snapshots</div>
|
<button
|
||||||
<button class="info-snapshot-btn" onclick={handleCreateVersion} title="Save snapshot">
|
type="button"
|
||||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
class="info-copy-path-btn"
|
||||||
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
|
class:copied={infoPathCopyState === 'copied'}
|
||||||
</svg>
|
class:error={infoPathCopyState === 'error'}
|
||||||
Save now
|
onclick={copyActiveNotePath}
|
||||||
</button>
|
aria-live="polite"
|
||||||
</div>
|
title="Copy note path"
|
||||||
{#if historyLoading}
|
>
|
||||||
<div class="info-empty">Loading...</div>
|
{#if infoPathCopyState === 'copied'}
|
||||||
{:else if historyVersions.length === 0}
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="m5 12 4 4L19 6" /></svg>
|
||||||
<div class="info-empty">No snapshots yet. They're created automatically as you edit.</div>
|
Copied
|
||||||
{:else}
|
{:else if infoPathCopyState === 'error'}
|
||||||
<div class="info-versions-list">
|
Copy failed
|
||||||
{#each historyVersions as v}
|
{:else}
|
||||||
<button
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2" /><path d="M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3" /></svg>
|
||||||
class="info-version-item"
|
Copy
|
||||||
class:active={historySelected?.timestamp === v.timestamp}
|
{/if}
|
||||||
onclick={() => { previewVersion(v); showHistory = true; showInfo = false; }}
|
</button>
|
||||||
>
|
|
||||||
<span class="info-version-date">{formatVersionDate(v.timestamp)}</span>
|
|
||||||
<span class="info-version-size">{formatVersionSize(v.size)}</span>
|
|
||||||
</button>
|
|
||||||
{/each}
|
|
||||||
</div>
|
</div>
|
||||||
|
<code class="info-note-path-value" title={$activeNotePath}>{$activeNotePath}</code>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -11402,13 +11418,6 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-section-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-section-label {
|
.info-section-label {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -11418,28 +11427,6 @@
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-section-header .info-section-label {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-snapshot-btn {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
background: none;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 4px;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 3px 7px;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-snapshot-btn:hover {
|
|
||||||
background: var(--bg-hover);
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-row {
|
.info-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
@@ -11472,6 +11459,64 @@
|
|||||||
max-width: 140px;
|
max-width: 140px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-note-path {
|
||||||
|
min-width: 0;
|
||||||
|
margin-top: 8px;
|
||||||
|
padding-top: 8px;
|
||||||
|
border-top: 1px solid var(--border-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-note-path-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-copy-path-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
margin: -3px -4px -3px 0;
|
||||||
|
padding: 3px 5px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--accent);
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-copy-path-btn:hover {
|
||||||
|
background: var(--accent-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-copy-path-btn:focus-visible {
|
||||||
|
outline: 2px solid var(--accent);
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-copy-path-btn.copied {
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-copy-path-btn.error {
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-note-path-value {
|
||||||
|
display: block;
|
||||||
|
margin-top: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Consolas, monospace;
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 1.4;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.info-tags {
|
.info-tags {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -11487,51 +11532,6 @@
|
|||||||
padding: 1px 5px;
|
padding: 1px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-empty {
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
line-height: 1.5;
|
|
||||||
padding: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-versions-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1px;
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-version-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 6px 8px;
|
|
||||||
border-radius: 5px;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: left;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-version-item:hover {
|
|
||||||
background: var(--bg-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-version-item.active {
|
|
||||||
background: var(--bg-active);
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-version-date {
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-version-size {
|
|
||||||
font-size: 11px;
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-container.mobile .editor-body-row:has(.info-panel) > .editor-body {
|
.editor-container.mobile .editor-body-row:has(.info-panel) > .editor-body {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { showInfo, appConfig, activeNotePath } from '$lib/stores/app';
|
import { showInfo } from '$lib/stores/app';
|
||||||
import { getVaultStats, findOrphanedAttachments, trashOrphanedAttachments } from '$lib/api';
|
import { getVaultStats, findOrphanedAttachments, trashOrphanedAttachments } from '$lib/api';
|
||||||
import type { OrphanAttachment } from '$lib/api';
|
import type { OrphanAttachment } from '$lib/api';
|
||||||
import { openUrl } from '$lib/api';
|
import { openUrl } from '$lib/api';
|
||||||
@@ -90,17 +90,11 @@
|
|||||||
let orphans = $state<OrphanAttachment[] | null>(null);
|
let orphans = $state<OrphanAttachment[] | null>(null);
|
||||||
let scanning = $state(false);
|
let scanning = $state(false);
|
||||||
let trashing = $state(false);
|
let trashing = $state(false);
|
||||||
let locationCopyState = $state<'idle' | 'copied' | 'error'>('idle');
|
|
||||||
let locationCopyTimer: ReturnType<typeof setTimeout> | null = null;
|
|
||||||
const orphanTotal = $derived((orphans ?? []).reduce((a, o) => a + o.size, 0));
|
const orphanTotal = $derived((orphans ?? []).reduce((a, o) => a + o.size, 0));
|
||||||
const diskLocation = $derived($activeNotePath ?? $appConfig?.active_vault ?? null);
|
|
||||||
|
|
||||||
getVersion().then(v => appVersion = v).catch(() => appVersion = '0.0.0');
|
getVersion().then(v => appVersion = v).catch(() => appVersion = '0.0.0');
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
if (locationCopyTimer) clearTimeout(locationCopyTimer);
|
|
||||||
locationCopyTimer = null;
|
|
||||||
locationCopyState = 'idle';
|
|
||||||
$showInfo = false;
|
$showInfo = false;
|
||||||
activeTab = isMobile ? 'about' : 'shortcuts';
|
activeTab = isMobile ? 'about' : 'shortcuts';
|
||||||
}
|
}
|
||||||
@@ -109,24 +103,6 @@
|
|||||||
openUrl(url).catch(console.error);
|
openUrl(url).catch(console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copyDiskLocation() {
|
|
||||||
if (!diskLocation) return;
|
|
||||||
|
|
||||||
if (locationCopyTimer) clearTimeout(locationCopyTimer);
|
|
||||||
try {
|
|
||||||
if (!navigator.clipboard?.writeText) throw new Error('Clipboard API unavailable');
|
|
||||||
await navigator.clipboard.writeText(diskLocation);
|
|
||||||
locationCopyState = 'copied';
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to copy disk location:', e);
|
|
||||||
locationCopyState = 'error';
|
|
||||||
}
|
|
||||||
locationCopyTimer = setTimeout(() => {
|
|
||||||
locationCopyState = 'idle';
|
|
||||||
locationCopyTimer = null;
|
|
||||||
}, 1600);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatSize(bytes: number): string {
|
function formatSize(bytes: number): string {
|
||||||
if (bytes < 1024) return `${bytes} B`;
|
if (bytes < 1024) return `${bytes} B`;
|
||||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
||||||
@@ -200,34 +176,6 @@
|
|||||||
<p class="app-version">v{appVersion}</p>
|
<p class="app-version">v{appVersion}</p>
|
||||||
<p class="app-description">A local markdown note-taking app.</p>
|
<p class="app-description">A local markdown note-taking app.</p>
|
||||||
|
|
||||||
{#if diskLocation}
|
|
||||||
<div class="disk-location">
|
|
||||||
<div class="disk-location-header">
|
|
||||||
<span class="disk-location-label">Disk location</span>
|
|
||||||
<button
|
|
||||||
class="copy-location-btn"
|
|
||||||
class:copied={locationCopyState === 'copied'}
|
|
||||||
class:error={locationCopyState === 'error'}
|
|
||||||
onclick={copyDiskLocation}
|
|
||||||
aria-live="polite"
|
|
||||||
title="Copy disk location"
|
|
||||||
>
|
|
||||||
{#if locationCopyState === 'copied'}
|
|
||||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="m5 12 4 4L19 6" /></svg>
|
|
||||||
Copied
|
|
||||||
{:else if locationCopyState === 'error'}
|
|
||||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12" /></svg>
|
|
||||||
Copy failed
|
|
||||||
{:else}
|
|
||||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2" /><path d="M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3" /></svg>
|
|
||||||
Copy
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<code class="disk-location-path" title={diskLocation}>{diskLocation}</code>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if stats}
|
{#if stats}
|
||||||
<div class="info-stats">
|
<div class="info-stats">
|
||||||
<div class="stat-row">
|
<div class="stat-row">
|
||||||
@@ -468,71 +416,6 @@
|
|||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.disk-location {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 18px;
|
|
||||||
padding: 11px 14px 12px;
|
|
||||||
border: 1px solid var(--border-light);
|
|
||||||
border-radius: 10px;
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disk-location-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disk-location-label {
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: 0.02em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disk-location-path {
|
|
||||||
display: block;
|
|
||||||
margin-top: 7px;
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Consolas, monospace;
|
|
||||||
font-size: 11px;
|
|
||||||
line-height: 1.45;
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-location-btn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
margin: -4px -6px -4px 0;
|
|
||||||
padding: 4px 6px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
background: transparent;
|
|
||||||
color: var(--accent);
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-location-btn:hover {
|
|
||||||
background: var(--accent-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-location-btn.copied {
|
|
||||||
color: var(--success);
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-location-btn.error {
|
|
||||||
color: var(--danger);
|
|
||||||
}
|
|
||||||
|
|
||||||
.disk-location + .info-stats {
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-stats {
|
.info-stats {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
@@ -784,11 +667,5 @@
|
|||||||
.info-header {
|
.info-header {
|
||||||
padding-top: calc(env(safe-area-inset-top, 12px) + 12px);
|
padding-top: calc(env(safe-area-inset-top, 12px) + 12px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-location-btn {
|
|
||||||
min-height: 44px;
|
|
||||||
margin-block: -10px;
|
|
||||||
padding-inline: 10px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user