mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-06 20:42:57 +08:00
🔨 Refactor(custom): refactor mini toolbox shorkey page
This commit is contained in:
@@ -1,57 +0,0 @@
|
||||
<template>
|
||||
<div class="toolbox-handler">
|
||||
<button class="handler-button" @click="() => props.handler(value)">
|
||||
{{ props.handlerText }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
interface IProps {
|
||||
status: string
|
||||
value: any
|
||||
handlerText: string
|
||||
handler: (value: any) => void | Promise<void>
|
||||
}
|
||||
|
||||
const props = defineProps<IProps>()
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'ToolboxHandler',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
.toolbox-handler {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.handler-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--color-accent);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: var(--transition-fast);
|
||||
font-family: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.handler-button:hover {
|
||||
background: var(--color-accent-hover);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.handler-button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
</style>
|
||||
23
src/renderer/components/toolbox/ToolboxHandler.vue
Normal file
23
src/renderer/components/toolbox/ToolboxHandler.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<div class="mt-0">
|
||||
<CustomButton type="primary" :text="props.handlerText" @click="() => props.handler(props.value)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import CustomButton from '@/components/common/CustomButton.vue'
|
||||
interface IProps {
|
||||
status: string
|
||||
value: any
|
||||
handlerText: string
|
||||
handler: (value: any) => void | Promise<void>
|
||||
}
|
||||
|
||||
const props = defineProps<IProps>()
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'ToolboxHandler',
|
||||
}
|
||||
</script>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<component :is="icon" class="toolbox-status-icon" :style="{ color }" />
|
||||
<component :is="icon" class="flex h-[20px] w-[20px] items-center justify-center rounded-full" :style="{ color }" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -43,14 +43,3 @@ export default {
|
||||
name: 'ToolboxStatusIcon',
|
||||
}
|
||||
</script>
|
||||
<style lang="stylus">
|
||||
.toolbox-status-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: var(--radius-full);
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
</style>
|
||||
@@ -4,15 +4,15 @@
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply m-0 h-full w-full overflow-hidden bg-bg p-0 font-[inherit] text-main;
|
||||
@apply m-0 h-full w-full overflow-hidden bg-transparent p-0 font-[inherit] text-main;
|
||||
}
|
||||
|
||||
html {
|
||||
@apply m-0 h-full p-0 w-full;
|
||||
@apply m-0 h-full p-0 w-full bg-transparent;
|
||||
}
|
||||
|
||||
#app {
|
||||
@apply h-full w-full;
|
||||
@apply h-full w-full bg-transparent;
|
||||
}
|
||||
|
||||
:focus {
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
<template>
|
||||
<div id="mini-page">
|
||||
<div
|
||||
id="mini-page"
|
||||
class="relative box-border h-screen w-screen cursor-pointer overflow-hidden border-4 border-white bg-accent bg-center bg-no-repeat text-center text-[40px] leading-[100vh]"
|
||||
:class="[osGlobal === 'linux' ? 'rounded-none bg-size-[100vh_100vw]' : 'rounded-full bg-size-[90vh_90vw]']"
|
||||
>
|
||||
<div
|
||||
id="upload-area"
|
||||
class="h-full w-full transition-all duration-200 ease-in-out"
|
||||
:class="{
|
||||
'is-dragover': dragover,
|
||||
uploading: isShowingProgress,
|
||||
linux: osGlobal === 'linux',
|
||||
'bg-[rgba(0,0,0,0.3)]': dragover,
|
||||
'bg-[linear-gradient(to_top,#409EFF_50%,#fff_51%)] bg-size-[200%]': isShowingProgress,
|
||||
'rounded-none': osGlobal === 'linux',
|
||||
'rounded-full': osGlobal !== 'linux',
|
||||
}"
|
||||
:style="{ backgroundPosition: '0 ' + progress + '%' }"
|
||||
@drop.prevent="onDrop"
|
||||
@@ -15,12 +21,13 @@
|
||||
<img
|
||||
v-if="!dragover && !isShowingProgress"
|
||||
:src="logoPath ? logoPath : './squareLogo.png'"
|
||||
style="border-radius: var(--radius-round); width: 100%; height: 100%"
|
||||
class="block h-full w-full [image-rendering:-webkit-optimize-contrast]"
|
||||
:class="osGlobal === 'linux' ? 'rounded-none' : 'rounded-full'"
|
||||
draggable="false"
|
||||
@dragstart.prevent
|
||||
/>
|
||||
<div id="upload-dragger" @dblclick="openUploadWindow">
|
||||
<input id="file-uploader" type="file" multiple @change="onChange" />
|
||||
<div id="upload-dragger" class="h-full" @dblclick="openUploadWindow">
|
||||
<input id="file-uploader" type="file" class="hidden" multiple @change="onChange" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -200,51 +207,3 @@ export default {
|
||||
name: 'MiniPage',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
html, body, #app
|
||||
background: transparent
|
||||
#mini-page
|
||||
background: #409EFF;
|
||||
overflow: hidden;
|
||||
color #FFF
|
||||
height 100vh
|
||||
width 100vw
|
||||
border-radius 50%
|
||||
text-align center
|
||||
line-height 100vh
|
||||
font-size 40px
|
||||
background-size 90vh 90vw
|
||||
background-position center center
|
||||
background-repeat no-repeat
|
||||
position relative
|
||||
border 4px solid #fff
|
||||
box-sizing border-box
|
||||
cursor pointer
|
||||
|
||||
&.linux
|
||||
border-radius 0
|
||||
background-size 100vh 100vw
|
||||
#upload-area
|
||||
height 100%
|
||||
width 100%
|
||||
border-radius 50%
|
||||
transition all .2s ease-in-out
|
||||
&.linux
|
||||
border-radius 0
|
||||
&.uploading
|
||||
background: linear-gradient(to top, #409EFF 50%, #fff 51%)
|
||||
background-size 200%
|
||||
#upload-dragger
|
||||
height 100%
|
||||
&.is-dragover
|
||||
background rgba(0,0,0,0.3)
|
||||
#file-uploader
|
||||
display none
|
||||
#mini-page img
|
||||
width 100%
|
||||
height 100%
|
||||
border-radius 50%
|
||||
display block
|
||||
image-rendering: -webkit-optimize-contrast
|
||||
</style>
|
||||
|
||||
@@ -1,39 +1,46 @@
|
||||
<template>
|
||||
<div class="rename-container">
|
||||
<div class="rename-card">
|
||||
<form @submit.prevent="confirmName">
|
||||
<div class="form-content">
|
||||
<div class="form-group">
|
||||
<div class="input-wrapper">
|
||||
<div class="flex h-full w-full items-center justify-center bg-bg-secondary p-2">
|
||||
<div
|
||||
class="flex h-full w-full flex-1 items-center overflow-hidden rounded-md border border-border bg-bg-tertiary shadow-md"
|
||||
>
|
||||
<form class="flex-1 p-4" @submit.prevent="confirmName">
|
||||
<div class="p-4">
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
ref="fileNameInput"
|
||||
v-model="form.fileName"
|
||||
type="text"
|
||||
class="form-input"
|
||||
class="box-border w-full rounded-md border border-border px-4 py-3 text-sm text-main focus:border-accent focus:outline-none [.input-error]:border-danger"
|
||||
:class="{ 'input-error': validationError }"
|
||||
:placeholder="t('pages.rename.placeholder')"
|
||||
autofocus
|
||||
@keyup.enter="confirmName"
|
||||
@input="clearValidationError"
|
||||
/>
|
||||
<button v-if="form.fileName" type="button" class="input-clear" @click="clearFileName">
|
||||
<button
|
||||
v-if="form.fileName"
|
||||
type="button"
|
||||
class="absolute top-1/2 -right-7 flex h-[24px] w-[24px] -translate-y-1/2 cursor-pointer items-center justify-center rounded-full border-none bg-danger/10 text-secondary hover:bg-danger/20 hover:text-white"
|
||||
@click="clearFileName"
|
||||
>
|
||||
<XIcon :size="16" />
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="validationError" class="validation-error">
|
||||
<div v-if="validationError" class="mt-2 flex justify-end gap-2 text-sm font-medium text-danger">
|
||||
{{ validationError }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-secondary" @click="cancel">
|
||||
{{ $t('common.cancel') }}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary" :disabled="!form.fileName.trim()">
|
||||
{{ $t('common.confirm') }}
|
||||
</button>
|
||||
<div class="flex flex-col items-center justify-center gap-3">
|
||||
<CustomButton class="w-[80%]" type="secondary" :text="t('common.cancel')" @click="cancel" />
|
||||
<CustomButton
|
||||
class="w-[80%]"
|
||||
type="primary"
|
||||
:text="t('common.confirm')"
|
||||
:disabled="!form.fileName.trim()"
|
||||
@click="confirmName"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -45,6 +52,7 @@ import { XIcon } from 'lucide-vue-next'
|
||||
import { nextTick, onBeforeMount, onBeforeUnmount, reactive, ref, useTemplateRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import CustomButton from '@/components/common/CustomButton.vue'
|
||||
import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME } from '@/utils/constant'
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -112,231 +120,9 @@ onBeforeUnmount(() => {
|
||||
window.electron.ipcRendererRemoveAllListeners(RENAME_FILE_NAME)
|
||||
})
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'RenamePage',
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.rename-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
background: var(--color-background-secondary);
|
||||
}
|
||||
|
||||
.rename-card {
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
background: var(--color-background-primary);
|
||||
box-shadow:
|
||||
0 20px 25px -5px rgb(0 0 0 / 10%),
|
||||
0 10px 10px -5px rgb(0 0 0 / 4%);
|
||||
}
|
||||
|
||||
/* Form */
|
||||
.form-content {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.875rem 1rem;
|
||||
padding-right: 2.5rem;
|
||||
width: 100%;
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-background-primary);
|
||||
transition: all 0.2s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
border-color: var(--color-accent);
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-accent-hover), transparent 60%);
|
||||
}
|
||||
|
||||
.form-input.input-error {
|
||||
border-color: #f56c6c;
|
||||
box-shadow: 0 0 0 2px rgb(245 108 108 / 20%);
|
||||
}
|
||||
|
||||
.input-clear {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0.75rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
color: var(--color-text-secondary);
|
||||
background: var(--color-background-tertiary);
|
||||
transition: all 0.2s ease;
|
||||
transform: translateY(-50%);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.input-clear:hover {
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-background-secondary);
|
||||
}
|
||||
|
||||
.validation-error {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
color: #f56c6c;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 2rem 2rem;
|
||||
background: var(--color-background-tertiary);
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.75rem 1.5rem;
|
||||
min-width: fit-content;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgb(0 0 0 / 15%);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: white;
|
||||
background: #409eff;
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: #66b1ff;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
border: 1px solid var(--color-border);
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-background-primary);
|
||||
}
|
||||
|
||||
.btn-secondary:hover:not(:disabled) {
|
||||
border-color: var(--color-accent);
|
||||
background: var(--color-background-secondary);
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (width <= 768px) {
|
||||
.rename-container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.rename-card {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
padding: 1rem 1.5rem 1.5rem;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.btn {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 480px) {
|
||||
.rename-container {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Focus styles for accessibility */
|
||||
.btn:focus-visible,
|
||||
.input-clear:focus-visible {
|
||||
outline: 2px solid var(--color-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.form-input:focus-visible {
|
||||
outline: 2px solid var(--color-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Animation for error state */
|
||||
@keyframes shake {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translateX(-4px);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translateX(4px);
|
||||
}
|
||||
}
|
||||
|
||||
.input-error {
|
||||
animation: shake 0.3s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,61 +1,82 @@
|
||||
<template>
|
||||
<div class="shortkey-container">
|
||||
<div class="relative flex h-full w-full items-center justify-center">
|
||||
<!-- Header -->
|
||||
<div class="shortkey-header">
|
||||
<div class="header-content">
|
||||
<KeyboardIcon :size="24" class="header-icon" />
|
||||
<div class="relative z-1 flex h-full w-full flex-col items-center justify-start gap-4 rounded-xl border-none p-4">
|
||||
<div
|
||||
class="flex w-full items-center justify-between gap-4 overflow-visible rounded-2xl border border-border-secondary p-4 shadow-md max-md:items-stretch"
|
||||
>
|
||||
<div class="flex flex-1 flex-wrap items-center gap-4 p-1">
|
||||
<KeyboardIcon :size="24" class="text-accent" />
|
||||
<div>
|
||||
<h1>{{ t('pages.shortKey.title') }}</h1>
|
||||
<p>{{ ' ' }}</p>
|
||||
<h1 class="m-0 text-2xl font-semibold tracking-tight text-main">{{ t('pages.shortKey.title') }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shortcuts Table Card -->
|
||||
<div class="shortkey-card">
|
||||
<div class="table-container">
|
||||
<table class="shortkey-table">
|
||||
<thead>
|
||||
<div
|
||||
class="relative flex h-full w-full flex-1 items-center justify-center overflow-hidden rounded-2xl border border-border-secondary p-1 shadow-md"
|
||||
>
|
||||
<div class="h-full w-full overflow-hidden rounded-xl shadow-sm">
|
||||
<table class="w-full table-auto bg-white text-left text-sm text-main">
|
||||
<thead class="bg-bg-secondary text-sm text-main uppercase">
|
||||
<tr>
|
||||
<th>{{ t('pages.shortKey.name') }}</th>
|
||||
<th>{{ t('pages.shortKey.bind') }}</th>
|
||||
<th>{{ t('pages.shortKey.status') }}</th>
|
||||
<th>{{ t('pages.shortKey.source') }}</th>
|
||||
<th>{{ t('pages.shortKey.handle') }}</th>
|
||||
<th class="px-6 py-4 font-semibold">{{ t('pages.shortKey.name') }}</th>
|
||||
<th class="px-6 py-4 font-semibold">{{ t('pages.shortKey.bind') }}</th>
|
||||
<th class="px-6 py-4 font-semibold">{{ t('pages.shortKey.status') }}</th>
|
||||
<th class="px-6 py-4 font-semibold">{{ t('pages.shortKey.source') }}</th>
|
||||
<th class="px-6 py-4 font-semibold">{{ t('pages.shortKey.handle') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in list" :key="item.name" class="table-row">
|
||||
<td class="name-cell">
|
||||
<div class="shortcut-name">
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
<tr
|
||||
v-for="(item, index) in list"
|
||||
:key="item.name"
|
||||
class="rounded-md transition-colors odd:bg-white even:bg-gray-50/30 hover:bg-accent/10"
|
||||
>
|
||||
<td class="px-6 py-4">
|
||||
<div class="text-sm font-semibold text-secondary">
|
||||
{{ item.label ? item.label : item.name }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="key-cell">
|
||||
<div class="key-binding">
|
||||
<kbd v-if="item.key" class="key-display">{{ item.key }}</kbd>
|
||||
<span v-else class="no-binding">{{ t('pages.shortKey.noBinding') }}</span>
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex gap-1">
|
||||
<kbd
|
||||
v-if="item.key"
|
||||
class="rounded-md border border-b-4 border-gray-300 bg-gray-100 px-2 py-1 text-xs font-semibold text-main shadow-sm"
|
||||
>{{ item.key }}</kbd
|
||||
>
|
||||
<span v-else class="text-xs text-secondary italic">{{ t('pages.shortKey.noBinding') }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="status-cell">
|
||||
<span class="status-badge" :class="{ 'status-enabled': item.enable, 'status-disabled': !item.enable }">
|
||||
<td class="px-6 py-4">
|
||||
<span
|
||||
:class="[
|
||||
'inline-flex items-center rounded-md p-2 text-sm font-semibold text-secondary',
|
||||
item.enable ? 'bg-success/10' : 'bg-danger/10',
|
||||
]"
|
||||
>
|
||||
{{ item.enable ? t('pages.shortKey.enabled') : t('pages.shortKey.disabled') }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="source-cell">
|
||||
<span class="source-name">{{ calcOriginShowName(item.from || '') }}</span>
|
||||
<td class="px-6 py-4 text-secondary">
|
||||
<span class="rounded-md bg-accent/10 p-2 font-bold text-main">{{
|
||||
calcOriginShowName(item.from || '')
|
||||
}}</span>
|
||||
</td>
|
||||
<td class="actions-cell">
|
||||
<div class="action-buttons">
|
||||
<td class="px-6 py-4 text-center">
|
||||
<div class="flex items-center justify-start gap-3">
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
:class="item.enable ? 'btn-danger' : 'btn-success'"
|
||||
:class="item.enable ? 'text-danger' : 'text-success'"
|
||||
class="w-[80px] rounded-md border border-border p-2 text-center text-sm font-semibold transition-colors hover:bg-accent/10"
|
||||
@click="toggleEnable(item)"
|
||||
>
|
||||
{{ item.enable ? t('pages.shortKey.disable') : t('pages.shortKey.enable') }}
|
||||
</button>
|
||||
<button class="btn btn-sm btn-secondary" @click="openKeyBindingDialog(item, index)">
|
||||
<Edit :size="14" />
|
||||
<button
|
||||
class="w-[80px] rounded-md border border-border p-2 text-center text-sm font-semibold text-secondary transition-colors hover:bg-accent/10"
|
||||
@click="openKeyBindingDialog(item, index)"
|
||||
>
|
||||
{{ t('pages.shortKey.edit') }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -65,53 +86,45 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Key Binding Modal -->
|
||||
<transition name="modal">
|
||||
<div v-if="keyBindingVisible" class="modal-overlay" @click.self="cancelKeyBinding">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">
|
||||
{{ t('pages.shortKey.changeUpload') }}
|
||||
</h3>
|
||||
<button class="modal-close" @click="cancelKeyBinding">
|
||||
<XIcon :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label>{{ t('pages.shortKey.keyBinding') }}</label>
|
||||
<CustomModal
|
||||
v-if="keyBindingVisible"
|
||||
v-model:visible="keyBindingVisible"
|
||||
:title="t('pages.shortKey.changeUpload')"
|
||||
width="600px"
|
||||
height="auto"
|
||||
>
|
||||
<div class="p-4">
|
||||
<label class="mb-4 block text-sm font-semibold text-secondary">{{ t('pages.shortKey.keyBinding') }}</label>
|
||||
<input
|
||||
v-model="shortKey"
|
||||
class="form-input key-input"
|
||||
class="box-border w-full rounded-md border border-border bg-bg-secondary p-3 text-center font-mono text-sm tracking-wider text-main focus:border-accent focus:outline-none"
|
||||
:placeholder="t('pages.shortKey.pressKeys')"
|
||||
readonly
|
||||
@keydown.prevent="keyDetect($event as KeyboardEvent)"
|
||||
/>
|
||||
<div class="input-hint">
|
||||
<div class="mt-2 text-center text-sm text-secondary">
|
||||
{{ t('pages.shortKey.pressHint') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @click="cancelKeyBinding">
|
||||
{{ $t('common.cancel') }}
|
||||
</button>
|
||||
<button class="btn btn-primary" @click="confirmKeyBinding">
|
||||
{{ $t('common.confirm') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<CustomButton type="secondary" :text="t('common.cancel')" @click="cancelKeyBinding" />
|
||||
<CustomButton type="primary" :text="t('common.confirm')" @click="confirmKeyBinding" />
|
||||
</template>
|
||||
</CustomModal>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Edit, KeyboardIcon, XIcon } from 'lucide-vue-next'
|
||||
import { KeyboardIcon } from 'lucide-vue-next'
|
||||
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import CustomButton from '@/components/common/CustomButton.vue'
|
||||
import CustomModal from '@/components/common/CustomModal.vue'
|
||||
import { getRawData } from '@/utils/common'
|
||||
import { configPaths } from '@/utils/configPaths'
|
||||
import { getConfig } from '@/utils/dataSender'
|
||||
@@ -191,5 +204,3 @@ export default {
|
||||
name: 'ShortkeyPage',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped src="./css/ShortKey.css"></style>
|
||||
|
||||
@@ -1,42 +1,46 @@
|
||||
<template>
|
||||
<div class="toolbox-container">
|
||||
<div class="relative no-scrollbar flex h-full w-full items-center justify-center bg-bg-tertiary">
|
||||
<div
|
||||
class="relative z-1 no-scrollbar flex h-full w-full flex-col items-center justify-start gap-6 overflow-auto rounded-xl border-none p-8 shadow-sm"
|
||||
>
|
||||
<!-- Header Card -->
|
||||
<div class="toolbox-card header-card">
|
||||
<div class="card-header">
|
||||
<div class="header-content">
|
||||
<img class="header-logo" :src="defaultLogo" alt="Toolbox Logo" />
|
||||
<div class="header-text">
|
||||
<h1 class="header-title">
|
||||
<div
|
||||
class="flex w-full items-center justify-between gap-4 overflow-visible rounded-2xl border border-border-secondary px-6 py-2 shadow-md max-md:items-stretch max-md:p-5"
|
||||
>
|
||||
<div class="flex flex-1 flex-wrap items-center gap-4 p-1">
|
||||
<div class="flex flex-1 items-center gap-4">
|
||||
<img class="h-[48px] w-[48px] rounded-full object-cover" :src="defaultLogo" alt="Toolbox Logo" />
|
||||
<div class="flex flex-col gap-1">
|
||||
<h1 class="m-0 text-2xl font-semibold text-main">
|
||||
{{ t('pages.toolbox.title') }}
|
||||
</h1>
|
||||
<p class="header-subtitle">
|
||||
<p class="m-0 text-sm text-secondary">
|
||||
{{ t('pages.toolbox.description') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<template v-if="progress !== 100">
|
||||
<button class="action-button" :class="{ disabled: isLoading }" :disabled="isLoading" @click="handleCheck">
|
||||
<span>{{ t('pages.toolbox.startScan') }}</span>
|
||||
</button>
|
||||
<CustomButton
|
||||
type="primary"
|
||||
:text="t('pages.toolbox.startScan')"
|
||||
:disabled="isLoading"
|
||||
@click="handleCheck"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="isAllSuccess">
|
||||
<div class="success-tips">
|
||||
<div class="border border-success/50 bg-bg-secondary px-5 py-3 text-sm font-semibold text-secondary">
|
||||
{{ t('pages.toolbox.success') }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="!isAllSuccess">
|
||||
<template v-if="canFixLength !== 0">
|
||||
<button class="action-button" @click="handleFix">
|
||||
<span>{{ t('pages.toolbox.startFix') }}</span>
|
||||
</button>
|
||||
<CustomButton type="secondary" :text="t('pages.toolbox.startFix')" @click="handleFix" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="cant-fix-container">
|
||||
<span class="cant-fix-text">{{ $t('pages.toolbox.autoFixFail') }}</span>
|
||||
<button class="action-button secondary small" @click="handleCheck">
|
||||
<span>{{ t('pages.toolbox.reScan') }}</span>
|
||||
</button>
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<span class="text-sm text-secondary">{{ $t('pages.toolbox.autoFixFail') }}</span>
|
||||
<CustomButton type="secondary" :text="t('pages.toolbox.reScan')" @click="handleCheck" />
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
@@ -45,47 +49,48 @@
|
||||
</div>
|
||||
|
||||
<!-- Progress Card -->
|
||||
<div class="toolbox-card progress-card">
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" :style="{ width: `${progress}%` }" />
|
||||
<div class="w-full rounded-md border border-border-secondary shadow-sm">
|
||||
<div class="flex items-center p-2">
|
||||
<div class="relative mr-3 h-2 flex-1 overflow-hidden rounded-full bg-surface-elevated">
|
||||
<div class="absolute top-0 left-0 h-2 rounded-full bg-success" :style="{ width: `${progress}%` }" />
|
||||
</div>
|
||||
<span class="progress-text">{{ Math.round(progress) }}%</span>
|
||||
<span class="text-sm text-secondary">{{ Math.round(progress) }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Items Card -->
|
||||
<div class="toolbox-card items-card">
|
||||
<div class="items-list">
|
||||
<div class="w-full flex-1 overflow-hidden rounded-md border border-border-secondary shadow-sm">
|
||||
<div class="h-full w-full overflow-auto p-4">
|
||||
<div class="border border-border-secondary shadow-sm">
|
||||
<div
|
||||
v-for="(item, key) in fixList"
|
||||
:key="key"
|
||||
class="item"
|
||||
class="border-b border-border-secondary last:border-0 hover:bg-surface-elevated"
|
||||
:class="{
|
||||
'item-active': activeTypes.includes(key),
|
||||
'item-error': item.status === IToolboxItemCheckStatus.ERROR,
|
||||
'item-success': item.status === IToolboxItemCheckStatus.SUCCESS,
|
||||
'item-loading': item.status === IToolboxItemCheckStatus.LOADING,
|
||||
'bg-surface-elevated': activeTypes.includes(key),
|
||||
'border-l-3 border-danger': item.status === IToolboxItemCheckStatus.ERROR,
|
||||
'border-l-3 border-success': item.status === IToolboxItemCheckStatus.SUCCESS,
|
||||
'border-l-3 border-accent': item.status === IToolboxItemCheckStatus.LOADING,
|
||||
}"
|
||||
>
|
||||
<div class="item-header" @click="toggleItem(key)">
|
||||
<div class="item-title">
|
||||
<div class="flex cursor-pointer items-center justify-between px-6 py-4" @click="toggleItem(key)">
|
||||
<div class="flex flex-1 items-center gap-3 text-sm font-semibold text-secondary">
|
||||
<span>{{ item.title }}</span>
|
||||
<toolbox-status-icon :status="item.status" />
|
||||
</div>
|
||||
<div class="item-chevron">
|
||||
<div class="flex items-center text-secondary">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="6,9 12,15 18,9" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="item-content">
|
||||
<div v-if="activeTypes.includes(key)" class="item-content">
|
||||
<div class="item-message">
|
||||
<div v-if="activeTypes.includes(key)" class="border-t border-border-secondary bg-surface p-2">
|
||||
<div class="mb-3 text-sm leading-[1.5] text-secondary">
|
||||
{{ item.msg || '' }}
|
||||
</div>
|
||||
<template v-if="item.handler && item.handlerText && item.value">
|
||||
<div class="item-actions">
|
||||
<div class="flex justify-start">
|
||||
<toolbox-handler
|
||||
:value="item.value"
|
||||
:status="item.status"
|
||||
@@ -100,14 +105,17 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onUnmounted, reactive, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import ToolboxHandler from '@/components/ToolboxHandler.vue'
|
||||
import ToolboxStatusIcon from '@/components/ToolboxStatusIcon.vue'
|
||||
import CustomButton from '@/components/common/CustomButton.vue'
|
||||
import ToolboxHandler from '@/components/toolbox/ToolboxHandler.vue'
|
||||
import ToolboxStatusIcon from '@/components/toolbox/ToolboxStatusIcon.vue'
|
||||
import useConfirm from '@/hooks/useConfirm'
|
||||
import { IRPCActionType, IToolboxItemCheckStatus, IToolboxItemType } from '@/utils/enum'
|
||||
|
||||
@@ -247,5 +255,3 @@ export default {
|
||||
name: 'ToolBoxPage',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped src="./css/ToolboxPage.css"></style>
|
||||
|
||||
@@ -142,10 +142,10 @@
|
||||
|
||||
<div
|
||||
v-if="showProgress"
|
||||
class="flex flex-wrap items-center justify-between gap-4 rounded-2xl border border-border-secondary p-0 shadow-md"
|
||||
class="flex w-full flex-wrap items-center justify-between gap-4 rounded-2xl border border-border-secondary p-0 shadow-md"
|
||||
>
|
||||
<div class="mx-2 my-2 w-full rounded-lg border border-border bg-surface p-4">
|
||||
<div class="mb-2 h-3 overflow-hidden rounded-lg bg-bg-secondary">
|
||||
<div class="flex w-full items-center gap-2 rounded-lg border border-border bg-surface p-2">
|
||||
<div class="h-3 flex-1 overflow-hidden rounded-lg bg-bg-secondary">
|
||||
<div
|
||||
class="h-full rounded-lg bg-[linear-gradient(90deg,var(--color-accent)_0%,var(--color-primary)_50%)] duration-fast ease-standard data-[error=true]:bg-danger data-[error=true]:bg-none"
|
||||
:data-error="showError"
|
||||
|
||||
@@ -1,427 +0,0 @@
|
||||
.shortkey-container {
|
||||
overflow-y: auto;
|
||||
padding: 1.5rem;
|
||||
min-height: 100vh;
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-background-secondary);
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.shortkey-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.shortkey-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.5rem;
|
||||
background: var(--color-surface);
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 10%);
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.shortkey-header h1 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.shortkey-header p {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* Card */
|
||||
.shortkey-card {
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
background: var(--color-background-primary);
|
||||
box-shadow: 0 2px 8px var(--color-border);
|
||||
}
|
||||
|
||||
/* Table */
|
||||
.table-container {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.shortkey-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.shortkey-table th {
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
padding: 1rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-background-tertiary);
|
||||
}
|
||||
|
||||
.shortkey-table td {
|
||||
border-bottom: 1px solid var(--color-border-secondary);
|
||||
padding: 1rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table-row:hover {
|
||||
background: var(--color-background-tertiary);
|
||||
}
|
||||
|
||||
.table-row:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Table Cells */
|
||||
.name-cell {
|
||||
width: 25%;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.key-cell {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.key-binding {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.key-display {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
font-family: monospace;
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-background-tertiary);
|
||||
box-shadow: 0 1px 2px rgb(0 0 0 / 10%);
|
||||
}
|
||||
|
||||
.no-binding {
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-secondary);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.status-cell {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 0.25rem 0.75rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-enabled {
|
||||
border: 1px solid rgb(103 194 58 / 20%);
|
||||
color: var(--color-success);
|
||||
background: rgb(103 194 58 / 10%);
|
||||
}
|
||||
|
||||
.status-disabled {
|
||||
border: 1px solid rgb(245 108 108 / 20%);
|
||||
color: var(--color-danger);
|
||||
background: rgb(245 108 108 / 10%);
|
||||
}
|
||||
|
||||
.source-cell {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.source-name {
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.actions-cell {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 0.5rem 0.875rem;
|
||||
min-width: fit-content;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s ease;
|
||||
gap: 0.375rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgb(0 0 0 / 15%);
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: white;
|
||||
background: var(--color-accent);
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: var(--color-accent-hover);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
border: 1px solid var(--color-border);
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-background-tertiary);
|
||||
}
|
||||
|
||||
.btn-secondary:hover:not(:disabled) {
|
||||
border-color: var(--color-accent);
|
||||
background: var(--color-background-secondary);
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
color: white;
|
||||
background: var(--color-success);
|
||||
}
|
||||
|
||||
.btn-success:hover:not(:disabled) {
|
||||
background: var(--color-success);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
color: white;
|
||||
background: var(--color-danger);
|
||||
}
|
||||
|
||||
.btn-danger:hover:not(:disabled) {
|
||||
background: var(--color-danger);
|
||||
}
|
||||
|
||||
/* Modal */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
background: rgb(0 0 0 / 50%);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
max-height: 90vh;
|
||||
background: var(--color-background-primary);
|
||||
box-shadow: 0 20px 25px -5px rgb(0 0 0 / 10%), 0 10px 10px -5px rgb(0 0 0 / 4%);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--color-border-secondary);
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
margin: 0;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-radius: var(--radius-sm);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
color: var(--color-text-secondary);
|
||||
background: var(--color-background-tertiary);
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-close:hover {
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-background-secondary);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid var(--color-border-secondary);
|
||||
padding: 1rem 1.5rem;
|
||||
background: var(--color-background-tertiary);
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
/* Form Elements */
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.form-input {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.75rem;
|
||||
width: 100%;
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-background-primary);
|
||||
transition: all 0.2s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
border-color: var(--color-accent);
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px rgb(64 158 255 / 20%);
|
||||
}
|
||||
|
||||
.key-input {
|
||||
font-family: monospace;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.input-hint {
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
text-align: center;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (width <= 768px) {
|
||||
.shortkey-container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.shortkey-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.shortkey-table th,
|
||||
.shortkey-table td {
|
||||
padding: 0.75rem 0.5rem;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.modal-header,
|
||||
.modal-body,
|
||||
.modal-footer {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 480px) {
|
||||
.shortkey-container {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.shortkey-header h1 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.shortkey-table {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.shortkey-table th,
|
||||
.shortkey-table td {
|
||||
padding: 0.5rem 0.375rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Focus styles for accessibility */
|
||||
.btn:focus-visible,
|
||||
.modal-close:focus-visible {
|
||||
outline: 2px solid var(--color-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.form-input:focus-visible {
|
||||
outline: 2px solid var(--color-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
@@ -1,363 +0,0 @@
|
||||
/* Global scrolling behavior */
|
||||
html, body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Container */
|
||||
.toolbox-container {
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
box-sizing: border-box;
|
||||
background: var(--color-background-tertiary);
|
||||
}
|
||||
|
||||
/* Card Base */
|
||||
.toolbox-card {
|
||||
overflow: auto;
|
||||
border: 1px solid var(--color-border-secondary);
|
||||
border-radius: var(--radius-xl);
|
||||
background: var(--color-background-tertiary);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: var(--transition-medium);
|
||||
}
|
||||
|
||||
.toolbox-card:hover {
|
||||
border-color: var(--color-border);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
/* Header Card */
|
||||
.header-card .card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--color-border-secondary);
|
||||
padding: 1rem 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header-logo {
|
||||
border-radius: var(--radius-lg);
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.header-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 0.75rem 1.25rem;
|
||||
min-width: 120px;
|
||||
font-size: 0.875rem;
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
background: var(--color-accent);
|
||||
transition: var(--transition-fast);
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.action-button:hover:not(.disabled) {
|
||||
background: var(--color-accent-hover);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.action-button.disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.action-button.secondary {
|
||||
border: 1px solid var(--color-border);
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-surface-elevated);
|
||||
}
|
||||
|
||||
.action-button.secondary:hover {
|
||||
border-color: var(--color-accent);
|
||||
color: var(--color-accent);
|
||||
background: var(--color-surface);
|
||||
}
|
||||
|
||||
.action-button.small {
|
||||
padding: 0.5rem 0.75rem;
|
||||
min-width: auto;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.success-tips {
|
||||
border: 1px solid rgb(103 194 58 / 30%);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 0.75rem 1.25rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-success);
|
||||
background: var(--color-background-secondary);
|
||||
}
|
||||
|
||||
.cant-fix-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.cant-fix-text {
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* Progress Card */
|
||||
.progress-card {
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 1rem 1.5rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-full);
|
||||
height: 8px;
|
||||
background: var(--color-surface-elevated);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
position: relative;
|
||||
border-radius: var(--radius-full);
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--color-accent) 0%, var(--color-accent-hover) 100%);
|
||||
transition: width 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.progress-fill::after {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(90deg, transparent 0%, rgb(255 255 255 / 20%) 50%, transparent 100%);
|
||||
content: '';
|
||||
animation: shimmer 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
min-width: 40px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
text-align: right;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* Items Card */
|
||||
.items-card {
|
||||
border-radius: var(--radius-lg);
|
||||
height: 230px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin: 0;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.items-list {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.item {
|
||||
border-bottom: 1px solid var(--color-border-secondary);
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
background: var(--color-surface-elevated);
|
||||
}
|
||||
|
||||
.item-active {
|
||||
background: var(--color-surface-elevated);
|
||||
}
|
||||
|
||||
.item-error {
|
||||
border-left: 3px solid var(--color-danger);
|
||||
}
|
||||
|
||||
.item-success {
|
||||
border-left: 3px solid var(--color-success);
|
||||
}
|
||||
|
||||
.item-loading {
|
||||
border-left: 3px solid var(--color-accent);
|
||||
}
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 1.5rem;
|
||||
transition: var(--transition-fast);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.item-header:hover {
|
||||
background: rgb(0 0 0 / 2%);
|
||||
}
|
||||
|
||||
.item-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex: 1;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.item-chevron {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--color-text-secondary);
|
||||
transition: var(--transition-fast);
|
||||
}
|
||||
|
||||
.item-active .item-chevron {
|
||||
transform: rotate(180deg);
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.item-content-enter-active,
|
||||
.item-content-leave-active {
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.item-content-enter-from,
|
||||
.item-content-leave-to {
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.item-content-enter-to,
|
||||
.item-content-leave-from {
|
||||
max-height: 200px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
border-top: 1px solid var(--color-border-secondary);
|
||||
padding: 0 1.5rem 1rem;
|
||||
background: var(--color-surface);
|
||||
}
|
||||
|
||||
.item-message {
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-secondary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.item-actions {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (width <= 768px) {
|
||||
.toolbox-container {
|
||||
padding: 0.75rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.header-card .card-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.cant-fix-container {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.item-header {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
padding: 0 1rem 0.75rem;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
.advancedAnimation {
|
||||
backdrop-filter: blur(5px) saturate(180%);
|
||||
background: rgb(255 255 255 / 20%);
|
||||
border: 1px solid rgb(255 255 255 / 30%);
|
||||
box-shadow: 0 8px 32px rgb(0 0 0 / 10%);
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
|
||||
|
||||
/* Modal - Base Styles (Used by ImageProcess Dialog) */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow-y: auto;
|
||||
padding: 2rem;
|
||||
background: rgb(0 0 0 / 50%);
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
overflow: hidden;
|
||||
margin: auto;
|
||||
border: 1px solid var(--color-border-secondary);
|
||||
border-radius: var(--radius-2xl);
|
||||
width: 90vw;
|
||||
max-width: 90vw;
|
||||
height: 85vh;
|
||||
max-height: 85vh;
|
||||
background: var(--color-background-tertiary);
|
||||
box-shadow: var(--shadow-xl);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--color-border-secondary);
|
||||
padding: 1rem 1.25rem;
|
||||
background: var(--color-background-tertiary);
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.modal-subtitle {
|
||||
margin: 0.25rem 0 0;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-round);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
color: var(--color-text-secondary);
|
||||
background: var(--color-surface-elevated);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.modal-close:hover {
|
||||
border-color: var(--color-danger);
|
||||
color: white;
|
||||
background: var(--color-danger);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
overflow-y: auto;
|
||||
max-height: calc(90vh - 90px);
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.modal-content::-webkit-scrollbar {
|
||||
width: 0.5rem;
|
||||
}
|
||||
|
||||
.modal-content::-webkit-scrollbar-track {
|
||||
background: var(--color-surface);
|
||||
}
|
||||
|
||||
.modal-content::-webkit-scrollbar-thumb {
|
||||
background: var(--color-border-secondary);
|
||||
border-radius: var(--radius-full);
|
||||
}
|
||||
|
||||
.modal-content::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--color-text-tertiary);
|
||||
}
|
||||
|
||||
@media (width <= 768px) {
|
||||
.modal-overlay {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.modal-header,
|
||||
.modal-content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-close:focus-visible {
|
||||
outline: 2px solid var(--color-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
Reference in New Issue
Block a user