quasar-ui-danx 0.4.95 → 0.4.99

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.
Files changed (33) hide show
  1. package/dist/danx.es.js +24452 -22880
  2. package/dist/danx.es.js.map +1 -1
  3. package/dist/danx.umd.js +133 -122
  4. package/dist/danx.umd.js.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/package.json +1 -1
  7. package/src/components/Utility/Buttons/ActionButton.vue +11 -3
  8. package/src/components/Utility/Code/CodeViewer.vue +219 -0
  9. package/src/components/Utility/Code/CodeViewerCollapsed.vue +34 -0
  10. package/src/components/Utility/Code/CodeViewerFooter.vue +53 -0
  11. package/src/components/Utility/Code/LanguageBadge.vue +122 -0
  12. package/src/components/Utility/Code/MarkdownContent.vue +251 -0
  13. package/src/components/Utility/Code/index.ts +5 -0
  14. package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +134 -38
  15. package/src/components/Utility/Files/CarouselHeader.vue +24 -0
  16. package/src/components/Utility/Files/FileMetadataDialog.vue +69 -0
  17. package/src/components/Utility/Files/FilePreview.vue +118 -166
  18. package/src/components/Utility/Files/index.ts +1 -0
  19. package/src/components/Utility/index.ts +1 -0
  20. package/src/composables/index.ts +5 -0
  21. package/src/composables/useCodeFormat.ts +199 -0
  22. package/src/composables/useCodeViewerCollapse.ts +125 -0
  23. package/src/composables/useCodeViewerEditor.ts +420 -0
  24. package/src/composables/useFilePreview.ts +119 -0
  25. package/src/composables/useTranscodeLoader.ts +68 -0
  26. package/src/helpers/formats/highlightSyntax.ts +327 -0
  27. package/src/helpers/formats/index.ts +3 -1
  28. package/src/helpers/formats/renderMarkdown.ts +338 -0
  29. package/src/styles/danx.scss +3 -0
  30. package/src/styles/themes/danx/code.scss +158 -0
  31. package/src/styles/themes/danx/index.scss +2 -0
  32. package/src/styles/themes/danx/markdown.scss +145 -0
  33. package/src/styles/themes/danx/scrollbar.scss +125 -0
@@ -0,0 +1,251 @@
1
+ <template>
2
+ <div class="dx-markdown-content">
3
+ <template v-for="(token, index) in tokens" :key="index">
4
+ <!-- Headings -->
5
+ <component
6
+ v-if="token.type === 'heading'"
7
+ :is="'h' + token.level"
8
+ v-html="parseInlineContent(token.content)"
9
+ />
10
+
11
+ <!-- Code blocks with syntax highlighting -->
12
+ <div
13
+ v-else-if="token.type === 'code_block'"
14
+ class="dx-markdown-code-block"
15
+ >
16
+ <!-- Language toggle badge (only for json/yaml) -->
17
+ <LanguageBadge
18
+ v-if="isToggleableLanguage(token.language)"
19
+ :format="getCodeBlockFormat(index, token.language)"
20
+ :available-formats="['json', 'yaml']"
21
+ :toggleable="true"
22
+ @change="(fmt) => setCodeBlockFormat(index, fmt)"
23
+ />
24
+ <LanguageBadge
25
+ v-else-if="token.language"
26
+ :format="token.language"
27
+ :available-formats="[]"
28
+ :toggleable="false"
29
+ />
30
+ <pre><code
31
+ :class="'language-' + getCodeBlockFormat(index, token.language)"
32
+ v-html="highlightCodeBlock(index, token.content, token.language)"
33
+ ></code></pre>
34
+ </div>
35
+
36
+ <!-- Blockquotes (recursive) -->
37
+ <blockquote
38
+ v-else-if="token.type === 'blockquote'"
39
+ v-html="renderBlockquote(token.content)"
40
+ />
41
+
42
+ <!-- Unordered lists -->
43
+ <ul v-else-if="token.type === 'ul'">
44
+ <li
45
+ v-for="(item, itemIndex) in token.items"
46
+ :key="itemIndex"
47
+ v-html="parseInlineContent(item)"
48
+ />
49
+ </ul>
50
+
51
+ <!-- Ordered lists -->
52
+ <ol
53
+ v-else-if="token.type === 'ol'"
54
+ :start="token.start"
55
+ >
56
+ <li
57
+ v-for="(item, itemIndex) in token.items"
58
+ :key="itemIndex"
59
+ v-html="parseInlineContent(item)"
60
+ />
61
+ </ol>
62
+
63
+ <!-- Horizontal rules -->
64
+ <hr v-else-if="token.type === 'hr'" />
65
+
66
+ <!-- Paragraphs -->
67
+ <p
68
+ v-else-if="token.type === 'paragraph'"
69
+ v-html="parseInlineContent(token.content).replace(/\n/g, '<br />')"
70
+ />
71
+ </template>
72
+ </div>
73
+ </template>
74
+
75
+ <script setup lang="ts">
76
+ import { computed, reactive } from "vue";
77
+ import { parse as parseYAML, stringify as stringifyYAML } from "yaml";
78
+ import { tokenizeBlocks, parseInline, renderMarkdown, BlockToken } from "../../../helpers/formats/renderMarkdown";
79
+ import { highlightJSON, highlightYAML } from "../../../helpers/formats/highlightSyntax";
80
+ import LanguageBadge from "./LanguageBadge.vue";
81
+
82
+ export interface MarkdownContentProps {
83
+ content: string;
84
+ defaultCodeFormat?: "json" | "yaml";
85
+ }
86
+
87
+ const props = withDefaults(defineProps<MarkdownContentProps>(), {
88
+ content: ""
89
+ });
90
+
91
+ // Track format overrides for each code block (for toggling json<->yaml)
92
+ const codeBlockFormats = reactive<Record<number, string>>({});
93
+ // Cache converted content for each code block
94
+ const convertedContent = reactive<Record<number, string>>({});
95
+
96
+ // Tokenize the markdown content
97
+ const tokens = computed<BlockToken[]>(() => {
98
+ if (!props.content) return [];
99
+ return tokenizeBlocks(props.content);
100
+ });
101
+
102
+ // Check if a language is toggleable (json or yaml)
103
+ function isToggleableLanguage(language: string): boolean {
104
+ if (!language) return false;
105
+ const lang = language.toLowerCase();
106
+ return lang === "json" || lang === "yaml";
107
+ }
108
+
109
+ // Get the current format for a code block (respecting user toggle, then default override, then original)
110
+ function getCodeBlockFormat(index: number, originalLanguage: string): string {
111
+ // If user has toggled this block, use their choice
112
+ if (codeBlockFormats[index]) {
113
+ return codeBlockFormats[index];
114
+ }
115
+
116
+ // If a default is set and this is a toggleable language, use the default
117
+ const lang = originalLanguage?.toLowerCase();
118
+ if (props.defaultCodeFormat && (lang === "json" || lang === "yaml")) {
119
+ return props.defaultCodeFormat;
120
+ }
121
+
122
+ // Otherwise use the original language
123
+ return lang || "text";
124
+ }
125
+
126
+ // Get converted content for a code block (handles initial conversion for defaultCodeFormat)
127
+ function getConvertedContent(index: number, originalContent: string, originalLang: string): string {
128
+ const format = getCodeBlockFormat(index, originalLang);
129
+
130
+ // If format matches original, no conversion needed
131
+ if (format === originalLang || !isToggleableLanguage(originalLang)) {
132
+ return originalContent;
133
+ }
134
+
135
+ // Convert from original to target format
136
+ try {
137
+ let parsed: unknown;
138
+
139
+ if (originalLang === "json") {
140
+ parsed = JSON.parse(originalContent);
141
+ } else if (originalLang === "yaml") {
142
+ parsed = parseYAML(originalContent);
143
+ }
144
+
145
+ if (parsed !== undefined) {
146
+ if (format === "json") {
147
+ return JSON.stringify(parsed, null, 2);
148
+ } else if (format === "yaml") {
149
+ return stringifyYAML(parsed as object);
150
+ }
151
+ }
152
+ } catch {
153
+ // Conversion failed, return original
154
+ }
155
+
156
+ return originalContent;
157
+ }
158
+
159
+ // Set format for a code block (converts content to the new format)
160
+ function setCodeBlockFormat(index: number, newFormat: string) {
161
+ const token = tokens.value[index];
162
+ if (token?.type !== "code_block") return;
163
+
164
+ const originalLang = token.language?.toLowerCase() || "json";
165
+ const current = getCodeBlockFormat(index, originalLang);
166
+
167
+ // No change needed if already in target format
168
+ if (current === newFormat) return;
169
+
170
+ // Convert the content
171
+ try {
172
+ // Use the currently displayed content (which may already be converted due to defaultCodeFormat)
173
+ const sourceContent = convertedContent[index] || getConvertedContent(index, token.content, originalLang);
174
+ let parsed: unknown;
175
+
176
+ // Parse from current format
177
+ if (current === "json") {
178
+ parsed = JSON.parse(sourceContent);
179
+ } else {
180
+ parsed = parseYAML(sourceContent);
181
+ }
182
+
183
+ // Convert to new format
184
+ if (newFormat === "json") {
185
+ convertedContent[index] = JSON.stringify(parsed, null, 2);
186
+ } else {
187
+ convertedContent[index] = stringifyYAML(parsed as object);
188
+ }
189
+
190
+ codeBlockFormats[index] = newFormat;
191
+ } catch {
192
+ // If conversion fails, just set the format without converting
193
+ codeBlockFormats[index] = newFormat;
194
+ }
195
+ }
196
+
197
+ // Highlight code block content based on format
198
+ function highlightCodeBlock(index: number, originalContent: string, originalLanguage: string): string {
199
+ const format = getCodeBlockFormat(index, originalLanguage);
200
+ const originalLang = originalLanguage?.toLowerCase() || "text";
201
+
202
+ // Get the content (converted if needed, or from cache if user toggled)
203
+ const content = convertedContent[index] || getConvertedContent(index, originalContent, originalLang);
204
+
205
+ // Apply syntax highlighting
206
+ switch (format) {
207
+ case "json":
208
+ return highlightJSON(content);
209
+ case "yaml":
210
+ return highlightYAML(content);
211
+ default:
212
+ // For other languages, just escape HTML
213
+ return content
214
+ .replace(/&/g, "&amp;")
215
+ .replace(/</g, "&lt;")
216
+ .replace(/>/g, "&gt;");
217
+ }
218
+ }
219
+
220
+ // Parse inline markdown (bold, italic, links, etc.)
221
+ function parseInlineContent(text: string): string {
222
+ return parseInline(text, true);
223
+ }
224
+
225
+ // Render blockquote content (can contain nested markdown)
226
+ function renderBlockquote(content: string): string {
227
+ return renderMarkdown(content);
228
+ }
229
+ </script>
230
+
231
+ <style lang="scss">
232
+ .dx-markdown-code-block {
233
+ position: relative;
234
+ margin: 1em 0;
235
+
236
+ pre {
237
+ margin: 0;
238
+ background: rgba(0, 0, 0, 0.3);
239
+ padding: 1em;
240
+ border-radius: 6px;
241
+ overflow-x: auto;
242
+
243
+ code {
244
+ background: transparent;
245
+ padding: 0;
246
+ font-size: 0.875em;
247
+ font-family: 'Fira Code', 'Monaco', monospace;
248
+ }
249
+ }
250
+ }
251
+ </style>
@@ -0,0 +1,5 @@
1
+ export { default as CodeViewer } from "./CodeViewer.vue";
2
+ export { default as CodeViewerCollapsed } from "./CodeViewerCollapsed.vue";
3
+ export { default as CodeViewerFooter } from "./CodeViewerFooter.vue";
4
+ export { default as LanguageBadge } from "./LanguageBadge.vue";
5
+ export { default as MarkdownContent } from "./MarkdownContent.vue";
@@ -10,58 +10,105 @@
10
10
  <!-- Header with filename and navigation -->
11
11
  <CarouselHeader
12
12
  v-if="currentFile"
13
- :filename="currentFile.filename || currentFile.name"
13
+ :filename="filename"
14
14
  :show-back-button="hasParent"
15
+ :show-metadata-button="hasMetadata"
16
+ :metadata-count="metadataKeyCount"
15
17
  :show-transcodes-button="!!(currentFile.transcodes && currentFile.transcodes.length > 0)"
16
18
  :transcodes-count="currentFile.transcodes?.length || 0"
17
19
  @back="navigateToParent"
20
+ @metadata="onMetadataClick"
18
21
  @transcodes="showTranscodeNav = true"
19
22
  />
20
23
 
21
- <!-- Carousel -->
22
- <div class="flex-grow relative">
23
- <div class="absolute inset-0">
24
+ <!-- Content Area with optional split panel -->
25
+ <div class="flex-grow flex relative min-h-0">
26
+ <!-- Carousel Section -->
27
+ <div class="flex-grow relative">
28
+ <div class="absolute inset-0">
29
+ <div
30
+ v-for="slide in visibleSlides"
31
+ :key="slide.file.id"
32
+ :class="[
33
+ 'absolute inset-0 flex items-center justify-center transition-opacity duration-300',
34
+ slide.isActive ? 'opacity-100 z-10' : 'opacity-0 z-0 pointer-events-none'
35
+ ]"
36
+ >
37
+ <FileRenderer
38
+ :file="slide.file"
39
+ :autoplay="slide.isActive"
40
+ />
41
+ </div>
42
+ </div>
43
+
44
+ <!-- Navigation Arrows -->
24
45
  <div
25
- v-for="slide in visibleSlides"
26
- :key="slide.file.id"
27
- :class="[
28
- 'absolute inset-0 flex items-center justify-center transition-opacity duration-300',
29
- slide.isActive ? 'opacity-100 z-10' : 'opacity-0 z-0 pointer-events-none'
30
- ]"
46
+ v-if="canNavigatePrevious"
47
+ class="absolute left-4 top-1/2 -translate-y-1/2 z-20"
31
48
  >
32
- <FileRenderer
33
- :file="slide.file"
34
- :autoplay="slide.isActive"
35
- />
49
+ <QBtn
50
+ round
51
+ size="lg"
52
+ class="bg-slate-800 text-white opacity-70 hover:opacity-100"
53
+ @click="navigatePrevious"
54
+ >
55
+ <ChevronLeftIcon class="w-8" />
56
+ </QBtn>
36
57
  </div>
37
- </div>
38
-
39
- <!-- Navigation Arrows -->
40
- <div
41
- v-if="canNavigatePrevious"
42
- class="absolute left-4 top-1/2 -translate-y-1/2 z-20"
43
- >
44
- <QBtn
45
- round
46
- size="lg"
47
- class="bg-slate-800 text-white opacity-70 hover:opacity-100"
48
- @click="navigatePrevious"
58
+ <div
59
+ v-if="canNavigateNext"
60
+ class="absolute right-4 top-1/2 -translate-y-1/2 z-20"
49
61
  >
50
- <ChevronLeftIcon class="w-8" />
51
- </QBtn>
62
+ <QBtn
63
+ round
64
+ size="lg"
65
+ class="bg-slate-800 text-white opacity-70 hover:opacity-100"
66
+ @click="navigateNext"
67
+ >
68
+ <ChevronRightIcon class="w-8" />
69
+ </QBtn>
70
+ </div>
52
71
  </div>
72
+
73
+ <!-- Split Metadata Panel -->
53
74
  <div
54
- v-if="canNavigateNext"
55
- class="absolute right-4 top-1/2 -translate-y-1/2 z-20"
75
+ v-if="metadataSplitMode && showMetadataDialog && hasMetadata"
76
+ class="w-[40%] max-w-[600px] min-w-[300px] bg-slate-900 border-l border-slate-700 flex flex-col"
56
77
  >
57
- <QBtn
58
- round
59
- size="lg"
60
- class="bg-slate-800 text-white opacity-70 hover:opacity-100"
61
- @click="navigateNext"
62
- >
63
- <ChevronRightIcon class="w-8" />
64
- </QBtn>
78
+ <div class="px-4 py-3 bg-slate-800 border-b border-slate-700 flex items-center justify-between flex-shrink-0">
79
+ <h3 class="text-slate-200 font-medium">Metadata</h3>
80
+ <div class="flex items-center gap-1">
81
+ <QBtn
82
+ flat
83
+ dense
84
+ round
85
+ size="sm"
86
+ class="text-slate-400 hover:text-white hover:bg-slate-700"
87
+ @click="undockToModal"
88
+ >
89
+ <UndockIcon class="w-4 h-4" />
90
+ <QTooltip>Undock to modal</QTooltip>
91
+ </QBtn>
92
+ <QBtn
93
+ flat
94
+ dense
95
+ round
96
+ size="sm"
97
+ class="text-slate-400 hover:text-white hover:bg-slate-700"
98
+ @click="showMetadataDialog = false"
99
+ >
100
+ <CloseIcon class="w-4 h-4" />
101
+ <QTooltip>Close</QTooltip>
102
+ </QBtn>
103
+ </div>
104
+ </div>
105
+ <div class="flex-1 min-h-0 p-4">
106
+ <CodeViewer
107
+ :model-value="filteredMetadata"
108
+ :readonly="true"
109
+ format="yaml"
110
+ />
111
+ </div>
65
112
  </div>
66
113
  </div>
67
114
 
@@ -88,19 +135,35 @@
88
135
  :transcodes="currentFile.transcodes"
89
136
  @select="onSelectTranscode"
90
137
  />
138
+
139
+ <!-- Metadata Dialog (only in modal mode) -->
140
+ <FileMetadataDialog
141
+ v-if="showMetadataDialog && !metadataSplitMode"
142
+ :filename="filename"
143
+ :mime-type="mimeType"
144
+ :metadata="filteredMetadata"
145
+ :show-dock-button="true"
146
+ @close="showMetadataDialog = false"
147
+ @dock="dockToSplit"
148
+ />
91
149
  </div>
92
150
  </QDialog>
93
151
  </template>
94
152
 
95
153
  <script setup lang="ts">
96
154
  import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/vue/outline";
155
+ import { FaSolidWindowMaximize as UndockIcon } from "danx-icon";
97
156
  import { ref } from "vue";
98
157
  import { useFileNavigation } from "../../../composables/useFileNavigation";
158
+ import { useFilePreview } from "../../../composables/useFilePreview";
99
159
  import { useKeyboardNavigation } from "../../../composables/useKeyboardNavigation";
100
160
  import { useVirtualCarousel } from "../../../composables/useVirtualCarousel";
161
+ import { getItem, setItem } from "../../../helpers";
101
162
  import { XIcon as CloseIcon } from "../../../svg";
102
163
  import { UploadedFile } from "../../../types";
164
+ import { CodeViewer } from "../Code";
103
165
  import CarouselHeader from "../Files/CarouselHeader.vue";
166
+ import FileMetadataDialog from "../Files/FileMetadataDialog.vue";
104
167
  import FileRenderer from "../Files/FileRenderer.vue";
105
168
  import ThumbnailStrip from "../Files/ThumbnailStrip.vue";
106
169
  import TranscodeNavigator from "../Files/TranscodeNavigator.vue";
@@ -155,4 +218,37 @@ function onSelectTranscode(transcode: UploadedFile, index: number) {
155
218
  diveInto(transcode, currentFile.value.transcodes);
156
219
  }
157
220
  }
221
+
222
+ // Metadata navigation
223
+ const showMetadataDialog = ref(false);
224
+ const METADATA_MODE_KEY = "danx-file-preview-metadata-mode";
225
+ const metadataSplitMode = ref(getItem(METADATA_MODE_KEY, false));
226
+
227
+ function onMetadataClick() {
228
+ // Toggle metadata visibility - mode determines whether it shows as split or modal
229
+ showMetadataDialog.value = !showMetadataDialog.value;
230
+ }
231
+
232
+ function dockToSplit() {
233
+ // Switch from modal to split view
234
+ metadataSplitMode.value = true;
235
+ setItem(METADATA_MODE_KEY, true);
236
+ // Keep showMetadataDialog true so it shows in split mode
237
+ }
238
+
239
+ function undockToModal() {
240
+ // Switch from split to modal view
241
+ metadataSplitMode.value = false;
242
+ setItem(METADATA_MODE_KEY, false);
243
+ // Keep showMetadataDialog true so it shows as modal
244
+ }
245
+
246
+ // Use file preview composable for metadata
247
+ const {
248
+ filename,
249
+ mimeType,
250
+ hasMetadata,
251
+ metadataKeyCount,
252
+ filteredMetadata
253
+ } = useFilePreview({ file: currentFile });
158
254
  </script>
@@ -22,6 +22,24 @@
22
22
  {{ filename }}
23
23
  </div>
24
24
 
25
+ <!-- Metadata Button -->
26
+ <QBtn
27
+ v-if="showMetadataButton"
28
+ flat
29
+ dense
30
+ class="bg-purple-700 text-purple-200 hover:bg-purple-600"
31
+ @click="$emit('metadata')"
32
+ >
33
+ <div class="flex items-center flex-nowrap gap-1">
34
+ <MetaIcon class="w-4" />
35
+ <QBadge
36
+ class="bg-purple-900 text-purple-200"
37
+ :label="metadataCount"
38
+ />
39
+ <span class="text-sm ml-1">Metadata</span>
40
+ </div>
41
+ </QBtn>
42
+
25
43
  <!-- Transcodes Button -->
26
44
  <QBtn
27
45
  v-if="showTranscodesButton"
@@ -57,16 +75,21 @@
57
75
 
58
76
  <script setup lang="ts">
59
77
  import { ArrowLeftIcon, FilmIcon } from "@heroicons/vue/outline";
78
+ import { FaSolidBarcode as MetaIcon } from "danx-icon";
60
79
  import { XIcon as CloseIcon } from "../../../svg";
61
80
 
62
81
  withDefaults(defineProps<{
63
82
  filename: string;
64
83
  showBackButton?: boolean;
84
+ showMetadataButton?: boolean;
85
+ metadataCount?: number;
65
86
  showTranscodesButton?: boolean;
66
87
  transcodesCount?: number;
67
88
  showCloseButton?: boolean;
68
89
  }>(), {
69
90
  showBackButton: false,
91
+ showMetadataButton: false,
92
+ metadataCount: 0,
70
93
  showTranscodesButton: false,
71
94
  transcodesCount: 0,
72
95
  showCloseButton: false
@@ -74,6 +97,7 @@ withDefaults(defineProps<{
74
97
 
75
98
  defineEmits<{
76
99
  'back': [];
100
+ 'metadata': [];
77
101
  'transcodes': [];
78
102
  'close': [];
79
103
  }>();
@@ -0,0 +1,69 @@
1
+ <template>
2
+ <InfoDialog
3
+ title="File Metadata"
4
+ :hide-done="true"
5
+ done-text="Close"
6
+ content-class="w-[80vw] h-[80vh] max-w-none"
7
+ @close="$emit('close')"
8
+ >
9
+ <div class="file-metadata-container h-full flex flex-col">
10
+ <!-- File info header -->
11
+ <div class="bg-sky-50 rounded-lg p-4 mb-4 flex-shrink-0">
12
+ <h4 class="text-lg font-semibold text-gray-900 mb-2">
13
+ {{ filename || 'Unnamed File' }}
14
+ </h4>
15
+ <div v-if="mimeType" class="text-sm text-gray-600">
16
+ Type: {{ mimeType }}
17
+ </div>
18
+ </div>
19
+
20
+ <!-- Metadata section -->
21
+ <div class="bg-white rounded-lg border border-gray-200 overflow-hidden flex-1 flex flex-col min-h-0">
22
+ <div class="bg-gray-50 px-4 py-3 border-b border-gray-200 flex-shrink-0 flex items-center justify-between">
23
+ <h4 class="text-base font-medium text-gray-900">
24
+ Metadata
25
+ </h4>
26
+ <QBtn
27
+ v-if="showDockButton"
28
+ flat
29
+ dense
30
+ round
31
+ size="sm"
32
+ class="text-gray-500 hover:text-gray-700 hover:bg-gray-200"
33
+ @click="$emit('dock')"
34
+ >
35
+ <DockSideIcon class="w-4 h-4" />
36
+ <QTooltip>Dock to side</QTooltip>
37
+ </QBtn>
38
+ </div>
39
+ <div class="p-4 flex-1 min-h-0 flex flex-col">
40
+ <CodeViewer
41
+ :model-value="metadata"
42
+ :readonly="true"
43
+ format="yaml"
44
+ />
45
+ </div>
46
+ </div>
47
+ </div>
48
+ </InfoDialog>
49
+ </template>
50
+
51
+ <script setup lang="ts">
52
+ import { FaSolidTableColumns as DockSideIcon } from "danx-icon";
53
+ import { CodeViewer } from "../Code";
54
+ import { InfoDialog } from "../Dialogs";
55
+
56
+ withDefaults(defineProps<{
57
+ filename: string;
58
+ mimeType?: string;
59
+ metadata: Record<string, unknown>;
60
+ showDockButton?: boolean;
61
+ }>(), {
62
+ showDockButton: false
63
+ });
64
+
65
+ defineEmits<{
66
+ close: [];
67
+ dock: [];
68
+ }>();
69
+ </script>