@quazardous/qdadm 2.31.0 → 2.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/styles/qdadm.css +1 -1
- package/package.json +8 -2
- package/src/components/display/EmailPreview.vue +227 -0
- package/src/components/display/EntityTimeline.vue +408 -0
- package/src/components/display/LogStream.vue +364 -0
- package/src/components/display/PayloadDiff.vue +236 -0
- package/src/components/display/PayloadViewer.vue +246 -0
- package/src/components/index.ts +8 -0
- package/src/components/layout/AppLayout.vue +31 -11
- package/src/components/layout/defaults/DefaultMenu.vue +23 -11
- package/src/components/tasks/TaskDialog.vue +95 -0
- package/src/components/tasks/TaskHost.vue +45 -0
- package/src/components/tasks/TaskTray.vue +54 -0
- package/src/composables/index.ts +11 -0
- package/src/composables/useListPage.filters.ts +26 -1
- package/src/composables/useLogStream.ts +264 -0
- package/src/composables/useNavigation.ts +39 -3
- package/src/email/index.ts +46 -0
- package/src/entity/storage/SdkStorage.ts +53 -1
- package/src/entity/storage/index.ts +2 -0
- package/src/i18n/defaults/core.en.generated.ts +85 -0
- package/src/i18n/defaults/core.en.yml +85 -0
- package/src/i18n/defaults/core.fr.generated.ts +85 -0
- package/src/i18n/defaults/core.fr.yml +85 -0
- package/src/index.ts +21 -0
- package/src/kernel/Kernel.vue.ts +9 -2
- package/src/kernel/KernelContext.types.ts +11 -1
- package/src/module/moduleRegistry.ts +17 -1
- package/src/styles/_email.scss +188 -0
- package/src/styles/_log-stream.scss +167 -0
- package/src/styles/_payload.scss +312 -0
- package/src/styles/_sidebar.scss +8 -0
- package/src/styles/_tasks.scss +212 -0
- package/src/styles/_timeline.scss +229 -0
- package/src/styles/index.scss +5 -0
- package/src/tasks/taskStore.ts +460 -0
- package/src/tasks/useTaskRunner.ts +52 -0
- package/src/utils/ansi.ts +127 -0
- package/src/utils/email.ts +270 -0
- package/src/utils/index.ts +32 -0
- package/src/utils/payload.ts +480 -0
|
@@ -71,6 +71,91 @@ export default `core:
|
|
|
71
71
|
notRegistered: non enregistrée
|
|
72
72
|
remove: Retirer
|
|
73
73
|
via: "via {origin}"
|
|
74
|
+
# LogStream (#4110)
|
|
75
|
+
log:
|
|
76
|
+
streaming: En cours
|
|
77
|
+
done: Terminé
|
|
78
|
+
stopped: Arrêté
|
|
79
|
+
failed: "Échec : {error}"
|
|
80
|
+
lines: "{count} lignes"
|
|
81
|
+
search: Chercher dans le log
|
|
82
|
+
previous: Occurrence précédente
|
|
83
|
+
next: Occurrence suivante
|
|
84
|
+
onlyMatches: Seulement les lignes trouvées
|
|
85
|
+
errors: "{count} erreurs"
|
|
86
|
+
firstError: Aller à la ligne d'erreur suivante
|
|
87
|
+
wrap: Couper les lignes longues
|
|
88
|
+
follow: Suivre la fin du log
|
|
89
|
+
download: Télécharger le log
|
|
90
|
+
newLines: "{count} nouvelles lignes, reprendre"
|
|
91
|
+
empty: Rien pour l'instant
|
|
92
|
+
# EntityTimeline (#4111)
|
|
93
|
+
timeline:
|
|
94
|
+
actors: Filtrer par acteur
|
|
95
|
+
loading: Chargement…
|
|
96
|
+
empty: Rien ne s'est encore passé
|
|
97
|
+
newEvents: "{count} nouveaux événements"
|
|
98
|
+
oldestFirst: Plus anciens d'abord
|
|
99
|
+
newestFirst: Plus récents d'abord
|
|
100
|
+
# PayloadViewer / PayloadDiff (#4112)
|
|
101
|
+
payload:
|
|
102
|
+
headers: "En-têtes ({count})"
|
|
103
|
+
headerRow: La première ligne est un en-tête
|
|
104
|
+
copy: Copier
|
|
105
|
+
copied: Copié dans le presse-papiers
|
|
106
|
+
copyFailed: Copie impossible
|
|
107
|
+
download: Télécharger
|
|
108
|
+
showAll: "Afficher les {count} lignes"
|
|
109
|
+
binary: "Contenu binaire, {size}. Premiers octets :"
|
|
110
|
+
malformed: "{format} invalide ({error}), affiché comme texte"
|
|
111
|
+
expected: Attendu
|
|
112
|
+
captured: Capturé
|
|
113
|
+
identical: Identiques
|
|
114
|
+
unchanged: "{count} lignes inchangées"
|
|
115
|
+
showSame: "Afficher les {count} lignes inchangées"
|
|
116
|
+
inline: En ligne
|
|
117
|
+
split: Côte à côte
|
|
118
|
+
# EmailPreview (#4113)
|
|
119
|
+
email:
|
|
120
|
+
from: De
|
|
121
|
+
to: À
|
|
122
|
+
cc: Cc
|
|
123
|
+
replyTo: Répondre à
|
|
124
|
+
date: Date
|
|
125
|
+
noSubject: (sans objet)
|
|
126
|
+
allHeaders: "Tous les en-têtes ({count})"
|
|
127
|
+
noParser: "Un message brut demande un parser : passer parseMime de @quazardous/qdadm/email. La source est affichée."
|
|
128
|
+
tab:
|
|
129
|
+
html: HTML
|
|
130
|
+
text: Texte
|
|
131
|
+
source: Source
|
|
132
|
+
imagesBlocked: "{count} images distantes bloquées"
|
|
133
|
+
loadImages: Charger les images
|
|
134
|
+
blockImages: Bloquer les images
|
|
135
|
+
empty: Ce mail n'a pas de contenu
|
|
136
|
+
unnamed: (sans nom)
|
|
137
|
+
inline: intégrée
|
|
138
|
+
download: Télécharger
|
|
139
|
+
# TaskRunner (#4114)
|
|
140
|
+
task:
|
|
141
|
+
status:
|
|
142
|
+
starting: Démarrage
|
|
143
|
+
queued: En attente
|
|
144
|
+
running: En cours
|
|
145
|
+
succeeded: Terminée
|
|
146
|
+
failed: Échouée
|
|
147
|
+
cancelled: Annulée
|
|
148
|
+
cancel: Annuler la tâche
|
|
149
|
+
close: Fermer
|
|
150
|
+
background: Continuer en arrière-plan
|
|
151
|
+
open: Ouvrir la tâche
|
|
152
|
+
readError: "Lecture de la tâche impossible ({error}), nouvel essai"
|
|
153
|
+
start: Lancer
|
|
154
|
+
dontStart: Ne pas lancer
|
|
155
|
+
typeToConfirm: "Taper {value} pour confirmer"
|
|
156
|
+
tray: "{count} tâches en cours"
|
|
157
|
+
trayDone: Tâches récentes
|
|
158
|
+
clear: Effacer les terminées
|
|
74
159
|
# The breadcrumb's View↔Edit toggle (useNavContext).
|
|
75
160
|
breadcrumb:
|
|
76
161
|
view: Voir
|
|
@@ -69,6 +69,91 @@ core:
|
|
|
69
69
|
notRegistered: non enregistrée
|
|
70
70
|
remove: Retirer
|
|
71
71
|
via: "via {origin}"
|
|
72
|
+
# LogStream (#4110)
|
|
73
|
+
log:
|
|
74
|
+
streaming: En cours
|
|
75
|
+
done: Terminé
|
|
76
|
+
stopped: Arrêté
|
|
77
|
+
failed: "Échec : {error}"
|
|
78
|
+
lines: "{count} lignes"
|
|
79
|
+
search: Chercher dans le log
|
|
80
|
+
previous: Occurrence précédente
|
|
81
|
+
next: Occurrence suivante
|
|
82
|
+
onlyMatches: Seulement les lignes trouvées
|
|
83
|
+
errors: "{count} erreurs"
|
|
84
|
+
firstError: Aller à la ligne d'erreur suivante
|
|
85
|
+
wrap: Couper les lignes longues
|
|
86
|
+
follow: Suivre la fin du log
|
|
87
|
+
download: Télécharger le log
|
|
88
|
+
newLines: "{count} nouvelles lignes, reprendre"
|
|
89
|
+
empty: Rien pour l'instant
|
|
90
|
+
# EntityTimeline (#4111)
|
|
91
|
+
timeline:
|
|
92
|
+
actors: Filtrer par acteur
|
|
93
|
+
loading: Chargement…
|
|
94
|
+
empty: Rien ne s'est encore passé
|
|
95
|
+
newEvents: "{count} nouveaux événements"
|
|
96
|
+
oldestFirst: Plus anciens d'abord
|
|
97
|
+
newestFirst: Plus récents d'abord
|
|
98
|
+
# PayloadViewer / PayloadDiff (#4112)
|
|
99
|
+
payload:
|
|
100
|
+
headers: "En-têtes ({count})"
|
|
101
|
+
headerRow: La première ligne est un en-tête
|
|
102
|
+
copy: Copier
|
|
103
|
+
copied: Copié dans le presse-papiers
|
|
104
|
+
copyFailed: Copie impossible
|
|
105
|
+
download: Télécharger
|
|
106
|
+
showAll: "Afficher les {count} lignes"
|
|
107
|
+
binary: "Contenu binaire, {size}. Premiers octets :"
|
|
108
|
+
malformed: "{format} invalide ({error}), affiché comme texte"
|
|
109
|
+
expected: Attendu
|
|
110
|
+
captured: Capturé
|
|
111
|
+
identical: Identiques
|
|
112
|
+
unchanged: "{count} lignes inchangées"
|
|
113
|
+
showSame: "Afficher les {count} lignes inchangées"
|
|
114
|
+
inline: En ligne
|
|
115
|
+
split: Côte à côte
|
|
116
|
+
# EmailPreview (#4113)
|
|
117
|
+
email:
|
|
118
|
+
from: De
|
|
119
|
+
to: À
|
|
120
|
+
cc: Cc
|
|
121
|
+
replyTo: Répondre à
|
|
122
|
+
date: Date
|
|
123
|
+
noSubject: (sans objet)
|
|
124
|
+
allHeaders: "Tous les en-têtes ({count})"
|
|
125
|
+
noParser: "Un message brut demande un parser : passer parseMime de @quazardous/qdadm/email. La source est affichée."
|
|
126
|
+
tab:
|
|
127
|
+
html: HTML
|
|
128
|
+
text: Texte
|
|
129
|
+
source: Source
|
|
130
|
+
imagesBlocked: "{count} images distantes bloquées"
|
|
131
|
+
loadImages: Charger les images
|
|
132
|
+
blockImages: Bloquer les images
|
|
133
|
+
empty: Ce mail n'a pas de contenu
|
|
134
|
+
unnamed: (sans nom)
|
|
135
|
+
inline: intégrée
|
|
136
|
+
download: Télécharger
|
|
137
|
+
# TaskRunner (#4114)
|
|
138
|
+
task:
|
|
139
|
+
status:
|
|
140
|
+
starting: Démarrage
|
|
141
|
+
queued: En attente
|
|
142
|
+
running: En cours
|
|
143
|
+
succeeded: Terminée
|
|
144
|
+
failed: Échouée
|
|
145
|
+
cancelled: Annulée
|
|
146
|
+
cancel: Annuler la tâche
|
|
147
|
+
close: Fermer
|
|
148
|
+
background: Continuer en arrière-plan
|
|
149
|
+
open: Ouvrir la tâche
|
|
150
|
+
readError: "Lecture de la tâche impossible ({error}), nouvel essai"
|
|
151
|
+
start: Lancer
|
|
152
|
+
dontStart: Ne pas lancer
|
|
153
|
+
typeToConfirm: "Taper {value} pour confirmer"
|
|
154
|
+
tray: "{count} tâches en cours"
|
|
155
|
+
trayDone: Tâches récentes
|
|
156
|
+
clear: Effacer les terminées
|
|
72
157
|
# The breadcrumb's View↔Edit toggle (useNavContext).
|
|
73
158
|
breadcrumb:
|
|
74
159
|
view: Voir
|
package/src/index.ts
CHANGED
|
@@ -327,6 +327,16 @@ export {
|
|
|
327
327
|
type LayoutMeta,
|
|
328
328
|
} from './composables/useLayoutResolver'
|
|
329
329
|
export { useSSEBridge, type UseSSEBridgeReturn } from './composables/useSSEBridge'
|
|
330
|
+
export {
|
|
331
|
+
useLogStream,
|
|
332
|
+
type LogSource,
|
|
333
|
+
type LogLine,
|
|
334
|
+
type LogPollResult,
|
|
335
|
+
type LogPushApi,
|
|
336
|
+
type LogStreamStatus,
|
|
337
|
+
type UseLogStreamOptions,
|
|
338
|
+
type UseLogStreamReturn,
|
|
339
|
+
} from './composables/useLogStream'
|
|
330
340
|
|
|
331
341
|
// Route-state persistence (#2146) — where a route remembers what it shows.
|
|
332
342
|
export {
|
|
@@ -396,6 +406,17 @@ export { useSidebarState, SIDEBAR_COLLAPSED_KEY } from './composables/useSidebar
|
|
|
396
406
|
// COMPONENTS
|
|
397
407
|
// ════════════════════════════════════════════════════════════════════════════
|
|
398
408
|
export * from './components/index'
|
|
409
|
+
export { useTaskRunner, taskAction } from './tasks/useTaskRunner'
|
|
410
|
+
export {
|
|
411
|
+
getTaskStore,
|
|
412
|
+
registerTaskKind,
|
|
413
|
+
type TaskRun,
|
|
414
|
+
type TaskStatus,
|
|
415
|
+
type TaskKind,
|
|
416
|
+
type TaskFollow,
|
|
417
|
+
type TaskConfirm,
|
|
418
|
+
type RunOptions,
|
|
419
|
+
} from './tasks/taskStore'
|
|
399
420
|
|
|
400
421
|
// ════════════════════════════════════════════════════════════════════════════
|
|
401
422
|
// MODULE SYSTEM (excluding types that conflict)
|
package/src/kernel/Kernel.vue.ts
CHANGED
|
@@ -3,6 +3,8 @@ import {
|
|
|
3
3
|
h,
|
|
4
4
|
defineComponent,
|
|
5
5
|
ref,
|
|
6
|
+
shallowRef,
|
|
7
|
+
markRaw,
|
|
6
8
|
onErrorCaptured,
|
|
7
9
|
type App,
|
|
8
10
|
type Component,
|
|
@@ -43,7 +45,10 @@ export function getQdadmDebugBarRef(): Ref<Component | null> {
|
|
|
43
45
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
46
|
const w = typeof window !== 'undefined' ? (window as any) : null
|
|
45
47
|
if (w && w[QDADM_DEBUG_BAR_GLOBAL_KEY]) return w[QDADM_DEBUG_BAR_GLOBAL_KEY]
|
|
46
|
-
|
|
48
|
+
// Shallow (#4117): a deep ref turns the component into a reactive proxy,
|
|
49
|
+
// and Vue warns "received a Component that was made a reactive object" on
|
|
50
|
+
// every render of the bar.
|
|
51
|
+
const r = shallowRef<Component | null>(null)
|
|
47
52
|
if (w) w[QDADM_DEBUG_BAR_GLOBAL_KEY] = r
|
|
48
53
|
return r
|
|
49
54
|
}
|
|
@@ -56,7 +61,9 @@ export const qdadmDebugBarRef: Ref<Component | null> = getQdadmDebugBarRef()
|
|
|
56
61
|
* constructor). Equivalent to `getQdadmDebugBarRef().value = component`.
|
|
57
62
|
*/
|
|
58
63
|
export function setQdadmDebugBar(component: Component | null): void {
|
|
59
|
-
|
|
64
|
+
// markRaw as well: the ref on window may be a deep one left by an older
|
|
65
|
+
// copy of this module (HMR keeps it across versions).
|
|
66
|
+
getQdadmDebugBarRef().value = component ? markRaw(component) : null
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
/**
|
|
@@ -87,7 +87,17 @@ export type DeferredFactory = () => Promise<void>
|
|
|
87
87
|
/** Navigation item configuration */
|
|
88
88
|
export interface NavItem {
|
|
89
89
|
section: string
|
|
90
|
-
route
|
|
90
|
+
/** Route name — exactly one of `route` and `href`. */
|
|
91
|
+
route?: string
|
|
92
|
+
/**
|
|
93
|
+
* A link out of the app instead of a route (#3675) — exactly one of `route`
|
|
94
|
+
* and `href`. An absolute URL opens in a new tab unless `target` says otherwise.
|
|
95
|
+
*/
|
|
96
|
+
href?: string
|
|
97
|
+
/** Where an `href` entry opens: default `_blank` for an absolute URL, same tab otherwise. */
|
|
98
|
+
target?: string
|
|
99
|
+
/** An `href` entry is shown only to whoever is granted this attribute. */
|
|
100
|
+
permission?: string
|
|
91
101
|
label: string
|
|
92
102
|
icon?: string
|
|
93
103
|
exact?: boolean
|
|
@@ -74,7 +74,16 @@ export interface ModuleRoute {
|
|
|
74
74
|
* Navigation item
|
|
75
75
|
*/
|
|
76
76
|
export interface NavItem {
|
|
77
|
-
route
|
|
77
|
+
route?: string
|
|
78
|
+
/**
|
|
79
|
+
* A link out of the app instead of a route (#3675) — exactly one of `route`
|
|
80
|
+
* and `href`. An absolute URL opens in a new tab unless `target` says otherwise.
|
|
81
|
+
*/
|
|
82
|
+
href?: string
|
|
83
|
+
/** Where an `href` entry opens: default `_blank` for an absolute URL, same tab otherwise. */
|
|
84
|
+
target?: string
|
|
85
|
+
/** An `href` entry is shown only to whoever is granted this attribute. */
|
|
86
|
+
permission?: string
|
|
78
87
|
label: string
|
|
79
88
|
icon?: string
|
|
80
89
|
entity?: string
|
|
@@ -209,6 +218,13 @@ const registry = {
|
|
|
209
218
|
* @param item - { section, route, icon, label, exact? }
|
|
210
219
|
*/
|
|
211
220
|
addNavItem(item: NavItemWithSection): void {
|
|
221
|
+
// An entry is a route or a link out, never both, never neither (#3675):
|
|
222
|
+
// an ambiguous one fails at boot rather than quietly in the menu.
|
|
223
|
+
if (!item.route === !item.href) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`[qdadm] nav item "${item.label}" in "${item.section}" needs exactly one of route and href`
|
|
226
|
+
)
|
|
227
|
+
}
|
|
212
228
|
const { section, ...navItem } = item
|
|
213
229
|
if (!navSections.has(section)) {
|
|
214
230
|
navSections.set(section, [])
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmailPreview (#4113)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
@use 'variables' as *;
|
|
6
|
+
|
|
7
|
+
.qd-email {
|
|
8
|
+
border: 1px solid var(--p-surface-200);
|
|
9
|
+
border-radius: $radius-sm;
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
min-width: 0;
|
|
12
|
+
background: var(--p-surface-0);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.qd-email-head {
|
|
16
|
+
padding: 0.75rem 1rem 0.5rem;
|
|
17
|
+
border-bottom: 1px solid var(--p-surface-200);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.qd-email-subject {
|
|
21
|
+
margin: 0 0 0.5rem;
|
|
22
|
+
font-size: 1.0625rem;
|
|
23
|
+
font-weight: 600;
|
|
24
|
+
overflow-wrap: anywhere;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.qd-email-main {
|
|
28
|
+
display: grid;
|
|
29
|
+
grid-template-columns: max-content minmax(0, 1fr);
|
|
30
|
+
gap: 0.125rem 0.75rem;
|
|
31
|
+
margin: 0;
|
|
32
|
+
font-size: $font-size-sm;
|
|
33
|
+
|
|
34
|
+
dt {
|
|
35
|
+
color: var(--p-text-muted-color);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
dd {
|
|
39
|
+
margin: 0;
|
|
40
|
+
overflow-wrap: anywhere;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.qd-email-headers {
|
|
45
|
+
margin-top: 0.5rem;
|
|
46
|
+
font-size: $font-size-xs;
|
|
47
|
+
|
|
48
|
+
summary {
|
|
49
|
+
cursor: pointer;
|
|
50
|
+
color: var(--p-text-muted-color);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
table {
|
|
54
|
+
margin-top: 0.25rem;
|
|
55
|
+
border-collapse: collapse;
|
|
56
|
+
font-family: $font-family-mono;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
th {
|
|
60
|
+
padding: 0.0625rem 1rem 0.0625rem 0;
|
|
61
|
+
text-align: left;
|
|
62
|
+
font-weight: 600;
|
|
63
|
+
white-space: nowrap;
|
|
64
|
+
vertical-align: top;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
td {
|
|
68
|
+
overflow-wrap: anywhere;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.qd-email-warning {
|
|
73
|
+
padding: 0.375rem 1rem;
|
|
74
|
+
background: var(--p-orange-50);
|
|
75
|
+
color: var(--p-orange-800);
|
|
76
|
+
font-size: $font-size-sm;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.qd-email-tabs {
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-wrap: wrap;
|
|
82
|
+
align-items: center;
|
|
83
|
+
gap: 0.25rem;
|
|
84
|
+
padding: 0 0.5rem;
|
|
85
|
+
border-bottom: 1px solid var(--p-surface-200);
|
|
86
|
+
font-size: $font-size-sm;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.qd-email-tab {
|
|
90
|
+
padding: 0.5rem 0.75rem;
|
|
91
|
+
border: none;
|
|
92
|
+
border-bottom: 2px solid transparent;
|
|
93
|
+
background: transparent;
|
|
94
|
+
color: var(--p-text-muted-color);
|
|
95
|
+
font: inherit;
|
|
96
|
+
cursor: pointer;
|
|
97
|
+
|
|
98
|
+
&--active {
|
|
99
|
+
border-bottom-color: var(--p-primary-color);
|
|
100
|
+
color: var(--p-text-color);
|
|
101
|
+
font-weight: 600;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.qd-email-spacer {
|
|
106
|
+
flex: 1;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.qd-email-blocked {
|
|
110
|
+
color: var(--p-text-muted-color);
|
|
111
|
+
font-size: $font-size-xs;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.qd-email-frame-wrap {
|
|
115
|
+
resize: vertical;
|
|
116
|
+
overflow: hidden;
|
|
117
|
+
min-height: 8rem;
|
|
118
|
+
background: #fff;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.qd-email-frame {
|
|
122
|
+
display: block;
|
|
123
|
+
width: 100%;
|
|
124
|
+
height: 100%;
|
|
125
|
+
border: 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.qd-email-text {
|
|
129
|
+
margin: 0;
|
|
130
|
+
padding: 0.75rem 1rem;
|
|
131
|
+
max-height: 32rem;
|
|
132
|
+
overflow: auto;
|
|
133
|
+
white-space: pre-wrap;
|
|
134
|
+
overflow-wrap: anywhere;
|
|
135
|
+
font-family: $font-family-mono;
|
|
136
|
+
font-size: 0.8125rem;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.qd-email-source {
|
|
140
|
+
white-space: pre;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.qd-email-empty {
|
|
144
|
+
padding: 0.75rem 1rem;
|
|
145
|
+
color: var(--p-text-muted-color);
|
|
146
|
+
font-style: italic;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.qd-email-attachments {
|
|
150
|
+
list-style: none;
|
|
151
|
+
margin: 0;
|
|
152
|
+
padding: 0.25rem 0.5rem;
|
|
153
|
+
border-top: 1px solid var(--p-surface-200);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.qd-email-attachment {
|
|
157
|
+
display: flex;
|
|
158
|
+
align-items: center;
|
|
159
|
+
gap: 0.5rem;
|
|
160
|
+
padding: 0.25rem;
|
|
161
|
+
font-size: $font-size-sm;
|
|
162
|
+
min-width: 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.qd-email-thumb {
|
|
166
|
+
width: 2.5rem;
|
|
167
|
+
height: 2.5rem;
|
|
168
|
+
object-fit: cover;
|
|
169
|
+
border-radius: $radius-sm;
|
|
170
|
+
border: 1px solid var(--p-surface-200);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.qd-email-clip {
|
|
174
|
+
width: 2.5rem;
|
|
175
|
+
text-align: center;
|
|
176
|
+
color: var(--p-text-muted-color);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.qd-email-att-name {
|
|
180
|
+
font-weight: 500;
|
|
181
|
+
overflow-wrap: anywhere;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.qd-email-att-meta {
|
|
185
|
+
flex: 1;
|
|
186
|
+
color: var(--p-text-muted-color);
|
|
187
|
+
font-size: $font-size-xs;
|
|
188
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LogStream (#4110)
|
|
3
|
+
* A terminal-like panel: dark in both themes, as logs are read.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
@use 'variables' as *;
|
|
7
|
+
|
|
8
|
+
.qd-log-stream {
|
|
9
|
+
--qd-log-bg: #1e1e1e;
|
|
10
|
+
--qd-log-fg: #d4d4d4;
|
|
11
|
+
--qd-log-muted: #858585;
|
|
12
|
+
--qd-log-mark: rgba(255, 213, 79, 0.45);
|
|
13
|
+
--qd-log-error-bg: rgba(241, 76, 76, 0.14);
|
|
14
|
+
--qd-log-current: rgba(59, 142, 234, 0.3);
|
|
15
|
+
|
|
16
|
+
position: relative;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
border: 1px solid var(--p-surface-300);
|
|
20
|
+
border-radius: $radius-sm;
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
min-width: 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.qd-log-toolbar {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-wrap: wrap;
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: 0.25rem 0.5rem;
|
|
30
|
+
padding: 0.25rem 0.5rem;
|
|
31
|
+
background: var(--p-surface-50);
|
|
32
|
+
border-bottom: 1px solid var(--p-surface-200);
|
|
33
|
+
font-size: $font-size-sm;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.qd-log-spacer-x {
|
|
37
|
+
flex: 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.qd-log-status {
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: 0.375rem;
|
|
44
|
+
font-weight: 500;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.qd-log-status-dot {
|
|
48
|
+
width: 0.5rem;
|
|
49
|
+
height: 0.5rem;
|
|
50
|
+
border-radius: 50%;
|
|
51
|
+
background: var(--p-surface-400);
|
|
52
|
+
|
|
53
|
+
.qd-log-status--streaming & {
|
|
54
|
+
background: var(--p-green-500);
|
|
55
|
+
animation: qd-log-pulse 1.2s ease-in-out infinite;
|
|
56
|
+
}
|
|
57
|
+
.qd-log-status--done & {
|
|
58
|
+
background: var(--p-blue-500);
|
|
59
|
+
}
|
|
60
|
+
.qd-log-status--error & {
|
|
61
|
+
background: var(--p-red-500);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@keyframes qd-log-pulse {
|
|
66
|
+
50% {
|
|
67
|
+
opacity: 0.3;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.qd-log-count,
|
|
72
|
+
.qd-log-search-count {
|
|
73
|
+
color: var(--p-text-muted-color);
|
|
74
|
+
font-size: $font-size-xs;
|
|
75
|
+
font-variant-numeric: tabular-nums;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.qd-log-search {
|
|
79
|
+
display: inline-flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
gap: 0.125rem;
|
|
82
|
+
|
|
83
|
+
.p-inputtext {
|
|
84
|
+
width: 12rem;
|
|
85
|
+
max-width: 40vw;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.qd-log-viewport {
|
|
90
|
+
overflow: auto;
|
|
91
|
+
background: var(--qd-log-bg);
|
|
92
|
+
color: var(--qd-log-fg);
|
|
93
|
+
font-family: $font-family-mono;
|
|
94
|
+
font-size: 0.8125rem;
|
|
95
|
+
line-height: var(--qd-log-line-height, 20px);
|
|
96
|
+
outline: none;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.qd-log-empty {
|
|
100
|
+
padding: 0.75rem 1rem;
|
|
101
|
+
color: var(--qd-log-muted);
|
|
102
|
+
font-style: italic;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.qd-log-line {
|
|
106
|
+
display: flex;
|
|
107
|
+
height: var(--qd-log-line-height, 20px);
|
|
108
|
+
white-space: pre;
|
|
109
|
+
padding-right: 0.75rem;
|
|
110
|
+
|
|
111
|
+
&--error {
|
|
112
|
+
background: var(--qd-log-error-bg);
|
|
113
|
+
}
|
|
114
|
+
&--current {
|
|
115
|
+
background: var(--qd-log-current);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.qd-log-stream--wrap & {
|
|
119
|
+
height: auto;
|
|
120
|
+
min-height: var(--qd-log-line-height, 20px);
|
|
121
|
+
white-space: pre-wrap;
|
|
122
|
+
word-break: break-word;
|
|
123
|
+
content-visibility: auto;
|
|
124
|
+
contain-intrinsic-size: auto var(--qd-log-line-height, 20px);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.qd-log-n {
|
|
129
|
+
flex: none;
|
|
130
|
+
min-width: 3.5rem;
|
|
131
|
+
padding: 0 0.75rem 0 0.5rem;
|
|
132
|
+
text-align: right;
|
|
133
|
+
color: var(--qd-log-muted);
|
|
134
|
+
user-select: none;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.qd-log-text {
|
|
138
|
+
flex: 1;
|
|
139
|
+
min-width: 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.qd-log-ts {
|
|
143
|
+
color: var(--qd-log-muted);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.qd-log-mark {
|
|
147
|
+
background: var(--qd-log-mark);
|
|
148
|
+
border-radius: 2px;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.qd-log-resume {
|
|
152
|
+
position: absolute;
|
|
153
|
+
bottom: 0.75rem;
|
|
154
|
+
left: 50%;
|
|
155
|
+
transform: translateX(-50%);
|
|
156
|
+
display: inline-flex;
|
|
157
|
+
align-items: center;
|
|
158
|
+
gap: 0.375rem;
|
|
159
|
+
padding: 0.25rem 0.75rem;
|
|
160
|
+
border: none;
|
|
161
|
+
border-radius: 999px;
|
|
162
|
+
background: var(--p-primary-color);
|
|
163
|
+
color: var(--p-primary-contrast-color);
|
|
164
|
+
font-size: $font-size-xs;
|
|
165
|
+
cursor: pointer;
|
|
166
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
|
|
167
|
+
}
|