@raclettejs/core 0.1.33-canary.2 → 0.1.33

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.
@@ -1,7 +1,7 @@
1
1
  import parentConfig from "../../eslint.config.mjs"
2
2
  import pluginVue from "eslint-plugin-vue"
3
+ import betterTailwind from "eslint-plugin-better-tailwindcss"
3
4
  import globals from "globals"
4
- // TODO: bring back tailwind with v4 support
5
5
 
6
6
  export default [
7
7
  ...parentConfig,
@@ -17,6 +17,26 @@ export default [
17
17
 
18
18
  // Vue config
19
19
  ...pluginVue.configs["flat/recommended"],
20
+
21
+ // Better-Tailwindcss: Tailwind v4 + `tw:` prefix is auto-detected from the
22
+ // CSS entryPoint. Scope to files that may contain class strings.
23
+ {
24
+ ...betterTailwind.configs.recommended,
25
+ files: ["**/*.{vue,ts,tsx,js,jsx,cjs,mjs}"],
26
+ },
27
+ {
28
+ files: ["**/*.{vue,ts,tsx,js,jsx,cjs,mjs}"],
29
+ settings: {
30
+ "better-tailwindcss": {
31
+ entryPoint: "src/orchestrator/assets/styles/tailwindStyles.css",
32
+ },
33
+ },
34
+ rules: {
35
+ // Tailwind v4 + `@source inline(...)` generates utilities dynamically,
36
+ // so unknown-class reports are noisy - downgrade to a warning.
37
+ "better-tailwindcss/no-unknown-classes": "warn",
38
+ },
39
+ },
20
40
  {
21
41
  files: ["**/*.vue"],
22
42
  languageOptions: {
@@ -47,7 +67,6 @@ export default [
47
67
  "vue/valid-v-show": "error",
48
68
  "vue/valid-v-slot": "error",
49
69
  "vue/valid-v-text": "error",
50
- // ...tailwindcss.configs.recommended.rules,
51
70
  },
52
71
  },
53
72
 
@@ -48,16 +48,16 @@
48
48
  "@vue/tsconfig": "0.9.0",
49
49
  "eslint": "9.35.0",
50
50
  "eslint-config-prettier": "9.1.2",
51
+ "eslint-plugin-better-tailwindcss": "4.5.0",
51
52
  "eslint-plugin-import": "2.32.0",
52
53
  "eslint-plugin-prefer-arrow-functions": "3.9.1",
53
- "eslint-plugin-tailwindcss": "3.18.2",
54
54
  "eslint-plugin-vue": "9.33.0",
55
55
  "globals": "17.4.0",
56
56
  "prettier": "3.8.1",
57
57
  "typescript": "5.9.3",
58
58
  "vite": "7.1.5",
59
59
  "vite-plugin-vuetify": "2.1.3",
60
- "vue-eslint-parser": "9.4.3",
60
+ "vue-eslint-parser": "10.3.0",
61
61
  "vue-tsc": "3.2.6"
62
62
  }
63
63
  }
@@ -4,7 +4,8 @@
4
4
  export default {
5
5
  background: "#212121",
6
6
  surface: "#212121",
7
- primary: "#0b6487",
7
+ "surface-light": "#212121",
8
+ primary: "#0FA2DB",
8
9
  "primary-darken-1": "#1279a2",
9
10
  "primary-darken-2": "#106689",
10
11
  "primary-darken-3": "#0e5470",
@@ -2,9 +2,10 @@
2
2
  * light theme
3
3
  */
4
4
  export default {
5
- background: "#F4F4F4",
6
- surface: "#F4F4F4",
7
- primary: "#148dbc",
5
+ background: "#ffffff",
6
+ surface: "#ffffff",
7
+ "surface-light": "#ffffff",
8
+ primary: "#127FAB",
8
9
  "primary-darken-1": "#107299",
9
10
  "primary-darken-2": "#0d5b7a",
10
11
  "primary-darken-3": "#0a455c",
@@ -23,7 +24,7 @@ export default {
23
24
  "secondary-darken-1": "#38a190",
24
25
  "secondary-darken-2": "#2b7b6d",
25
26
  error: "#B00020",
26
- info: "#2196F3",
27
+ info: "#1973BE",
27
28
  success: "#4CAF50",
28
29
  warning: "#FB8C00",
29
30
  "surface-2": "#F4F4F4",
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div
3
- class="tw:max-w-full tw:bg-gray-50 tw:dark:bg-[#252525] tw:min-h-fit"
3
+ class="tw:max-w-full tw:min-h-fit"
4
4
  :class="{
5
5
  'tw:p-4':
6
6
  slotLayout !== 'default' &&
@@ -8,16 +8,15 @@
8
8
  :min-width="360"
9
9
  >
10
10
  <template #activator="{ props: menuProps }">
11
- <slot v-if="$slots.default" v-bind="triggerSlotProps" />
11
+ <slot v-if="$slots.default" v-bind="{ ...triggerSlotProps, menuProps }" />
12
12
  <v-btn
13
13
  v-else
14
14
  v-bind="{ ...menuProps, ...$attrs }"
15
15
  :disabled="disabled"
16
- :variant="$attrs.variant || 'tonal'"
17
- color="info"
16
+ :variant="$attrs.variant || 'outlined'"
17
+ color="primary"
18
18
  prepend-icon="mdi-export-variant"
19
- class="tw:font-medium"
20
- density="compact"
19
+ class="tw:font-medium tw:mb-2"
21
20
  @click="open"
22
21
  >
23
22
  {{ buttonLabel }}
@@ -26,58 +25,64 @@
26
25
 
27
26
  <v-card rounded="lg" elevation="8" :style="{ width: '360px' }">
28
27
  <!-- Header -->
29
- <div class="tw:flex tw:items-center tw:justify-between tw:px-4 tw:pt-4 tw:pb-2">
30
- <div class="tw:flex tw:items-center tw:gap-2">
31
- <v-icon icon="mdi-export-variant" size="18" color="primary" />
32
- <span class="tw:text-sm tw:font-semibold tw:tracking-wide tw:uppercase tw:opacity-70">
33
- {{ $t("core.dataExporter.title") }}
34
- </span>
28
+ <slot name="header" v-bind="headerSlotProps">
29
+ <div class="tw:flex tw:items-center tw:justify-between tw:px-4 tw:pt-4 tw:pb-2">
30
+ <div class="tw:flex tw:items-center tw:gap-2">
31
+ <v-icon icon="mdi-export-variant" size="18" color="primary" />
32
+ <span class="tw:text-sm tw:font-semibold tw:tracking-wide tw:uppercase tw:opacity-70">
33
+ {{ $t("core.dataExporter.title") }}
34
+ </span>
35
+ </div>
36
+ <v-btn icon="mdi-close" variant="text" size="x-small" @click="isOpen = false" />
35
37
  </div>
36
- <v-btn icon="mdi-close" variant="text" size="x-small" @click="isOpen = false" />
37
- </div>
38
+ </slot>
38
39
 
39
40
  <v-divider />
40
41
 
41
42
  <!-- Format tabs -->
42
- <div class="tw:px-4 tw:pt-3 tw:pb-1">
43
- <v-chip-group
44
- v-model="activeFormatId"
45
- mandatory
46
- selected-class="text-primary"
47
- class="tw:gap-1"
48
- >
49
- <v-chip
50
- v-for="fmt in resolvedFormats"
51
- :key="fmt.id"
52
- :value="fmt.id"
53
- size="small"
54
- variant="tonal"
55
- class="tw:font-mono tw:text-xs"
43
+ <slot name="format-selector" v-bind="formatSelectorSlotProps">
44
+ <div class="tw:px-4 tw:pt-3 tw:pb-1">
45
+ <v-chip-group
46
+ v-model="activeFormatId"
47
+ mandatory
48
+ selected-class="text-primary"
49
+ class="tw:gap-1"
56
50
  >
57
- {{ fmt.label }}
58
- </v-chip>
59
- </v-chip-group>
60
- </div>
51
+ <v-chip
52
+ v-for="fmt in resolvedFormats"
53
+ :key="fmt.id"
54
+ :value="fmt.id"
55
+ size="small"
56
+ variant="tonal"
57
+ class="tw:font-mono tw:text-xs"
58
+ >
59
+ {{ fmt.label }}
60
+ </v-chip>
61
+ </v-chip-group>
62
+ </div>
63
+ </slot>
61
64
 
62
65
  <!-- Data scope switch — only shown when both items AND meta exist -->
63
66
  <div
64
67
  v-if="showScopeSwitch"
65
68
  class="tw:px-4 tw:py-1 tw:flex tw:items-center tw:justify-between tw:gap-2"
66
69
  >
67
- <span class="tw:text-xs tw:opacity-60 tw:uppercase tw:tracking-wide">
68
- {{ $t("core.dataExporter.scopeLabel") }}
69
- </span>
70
- <v-btn-toggle v-model="selectedScope" divided density="compact" mandatory>
71
- <v-btn value="combined" size="x-small">
72
- {{ $t("core.dataExporter.scopeCombined") }}
73
- </v-btn>
74
- <v-btn value="items" size="x-small">
75
- {{ $t("core.dataExporter.scopeItems") }}
76
- </v-btn>
77
- <v-btn value="meta" size="x-small">
78
- {{ $t("core.dataExporter.scopeMeta") }}
79
- </v-btn>
80
- </v-btn-toggle>
70
+ <slot name="scope-switch" v-bind="scopeSwitchSlotProps">
71
+ <span class="tw:text-xs tw:opacity-60 tw:uppercase tw:tracking-wide">
72
+ {{ $t("core.dataExporter.scopeLabel") }}
73
+ </span>
74
+ <v-btn-toggle v-model="selectedScope" divided density="compact" mandatory>
75
+ <v-btn value="combined" size="x-small">
76
+ {{ $t("core.dataExporter.scopeCombined") }}
77
+ </v-btn>
78
+ <v-btn value="items" size="x-small">
79
+ {{ $t("core.dataExporter.scopeItems") }}
80
+ </v-btn>
81
+ <v-btn value="meta" size="x-small">
82
+ {{ $t("core.dataExporter.scopeMeta") }}
83
+ </v-btn>
84
+ </v-btn-toggle>
85
+ </slot>
81
86
  </div>
82
87
 
83
88
  <!-- No formats warning -->
@@ -89,105 +94,111 @@
89
94
  <!-- Preview pane -->
90
95
  <div v-else class="tw:px-4 tw:pt-1 tw:pb-4">
91
96
  <!-- Preview toggle -->
92
- <v-btn
93
- variant="text"
94
- density="compact"
95
- class="tw:mb-1.5 tw:w-full tw:justify-start tw:normal-case tw:text-xs tw:opacity-60 hover:tw:opacity-90"
96
- @click="previewOpen = !previewOpen"
97
- >
98
- <template #prepend>
99
- <v-icon
100
- :icon="previewOpen ? 'mdi-chevron-down' : 'mdi-chevron-right'"
101
- size="14"
102
- />
103
- </template>
104
- {{ $t("core.dataExporter.preview") }}
105
- </v-btn>
97
+ <slot name="preview-toggle" v-bind="previewToggleSlotProps">
98
+ <v-btn
99
+ variant="text"
100
+ density="compact"
101
+ class="tw:mb-1.5 tw:w-full tw:justify-start tw:normal-case tw:text-xs tw:opacity-60 hover:tw:opacity-90"
102
+ @click="previewOpen = !previewOpen"
103
+ >
104
+ <template #prepend>
105
+ <v-icon
106
+ :icon="previewOpen ? 'mdi-chevron-down' : 'mdi-chevron-right'"
107
+ size="14"
108
+ />
109
+ </template>
110
+ {{ $t("core.dataExporter.preview") }}
111
+ </v-btn>
112
+ </slot>
106
113
 
107
114
  <!-- Fixed-height container prevents width reflow when toggling -->
108
115
  <div class="tw:overflow-hidden" :style="{ height: previewOpen ? 'auto' : '0px', minHeight: previewOpen ? '0px' : undefined }">
109
116
  <v-expand-transition>
110
117
  <div v-show="previewOpen">
111
- <!-- Serialization error -->
112
- <v-alert
113
- v-if="!serializeResult.ok"
114
- type="error"
115
- variant="tonal"
116
- density="compact"
117
- class="tw:mb-2 tw:font-mono tw:text-xs"
118
- >
119
- <pre class="tw:whitespace-pre-wrap tw:break-all">{{ serializeResult.error }}</pre>
120
- </v-alert>
121
-
122
- <!-- Preview code block -->
123
- <div
124
- v-else
125
- class="tw:rounded-lg tw:overflow-hidden tw:border tw:border-black/10 dark:tw:border-white/10"
126
- >
127
- <!-- Toolbar -->
128
- <div
129
- class="tw:flex tw:items-center tw:justify-between tw:px-3 tw:py-1.5 tw:bg-black/5 dark:tw:bg-white/5 tw:border-b tw:border-black/10 dark:tw:border-white/10"
118
+ <slot name="preview-content" v-bind="previewContentSlotProps">
119
+ <!-- Serialization error -->
120
+ <v-alert
121
+ v-if="!serializeResult.ok"
122
+ type="error"
123
+ variant="tonal"
124
+ density="compact"
125
+ class="tw:mb-2 tw:font-mono tw:text-xs"
130
126
  >
131
- <span class="tw:font-mono tw:text-[10px] tw:uppercase tw:tracking-widest tw:opacity-50">
132
- {{ activeFormatId }}
133
- </span>
134
- <span class="tw:font-mono tw:text-[10px] tw:opacity-40">
135
- {{ previewLines.lineCount }}
136
- {{ $t("core.dataExporter.lines") }}
137
- </span>
138
- </div>
127
+ <pre class="tw:whitespace-pre-wrap tw:break-all">{{ serializeResult.error }}</pre>
128
+ </v-alert>
139
129
 
140
- <!-- Code content -->
141
- <pre
142
- class="tw:text-xs tw:font-mono tw:p-3 tw:max-h-52 tw:overflow-auto tw:leading-relaxed tw:bg-black/[0.03] dark:tw:bg-white/[0.03] tw:m-0"
143
- >{{ previewLines.text }}</pre>
144
-
145
- <!-- Truncation notice -->
130
+ <!-- Preview code block -->
146
131
  <div
147
- v-if="previewLines.truncated"
148
- class="tw:text-[10px] tw:text-center tw:py-1 tw:opacity-40 tw:bg-black/5 dark:tw:bg-white/5 tw:border-t tw:border-black/10 dark:tw:border-white/10"
132
+ v-else
133
+ class="tw:rounded-lg tw:overflow-hidden tw:border tw:border-black/10 dark:tw:border-white/10"
149
134
  >
150
- {{ $t("core.dataExporter.previewTruncatedAt", { count: PREVIEW_LINE_LIMIT }) }}
135
+ <!-- Toolbar -->
136
+ <div
137
+ class="tw:flex tw:items-center tw:justify-between tw:px-3 tw:py-1.5 tw:bg-black/5 dark:tw:bg-white/5 tw:border-b tw:border-black/10 dark:tw:border-white/10"
138
+ >
139
+ <span class="tw:font-mono tw:text-[10px] tw:uppercase tw:tracking-widest tw:opacity-50">
140
+ {{ activeFormatId }}
141
+ </span>
142
+ <span class="tw:font-mono tw:text-[10px] tw:opacity-40">
143
+ {{ previewLines.lineCount }}
144
+ {{ $t("core.dataExporter.lines") }}
145
+ </span>
146
+ </div>
147
+
148
+ <!-- Code content -->
149
+ <pre
150
+ class="tw:text-xs tw:font-mono tw:p-3 tw:max-h-52 tw:overflow-auto tw:leading-relaxed tw:bg-black/[0.03] dark:tw:bg-white/[0.03] tw:m-0"
151
+ >{{ previewLines.text }}</pre>
152
+
153
+ <!-- Truncation notice -->
154
+ <div
155
+ v-if="previewLines.truncated"
156
+ class="tw:text-[10px] tw:text-center tw:py-1 tw:opacity-40 tw:bg-black/5 dark:tw:bg-white/5 tw:border-t tw:border-black/10 dark:tw:border-white/10"
157
+ >
158
+ {{ $t("core.dataExporter.previewTruncatedAt", { count: PREVIEW_LINE_LIMIT }) }}
159
+ </div>
151
160
  </div>
152
- </div>
161
+ </slot>
153
162
  </div>
154
163
  </v-expand-transition>
155
164
  </div>
156
165
 
157
166
  <!-- Action Buttons -->
158
- <div class="tw:flex tw:gap-2 tw:mt-3">
159
- <v-btn
160
- v-if="clipboardSupported"
161
- :disabled="!serializeResult.ok"
162
- :color="copyState === 'success' ? 'success' : copyState === 'error' ? 'error' : 'primary'"
163
- variant="tonal"
164
- size="small"
165
- class="tw:flex-1 tw:font-medium"
166
- :prepend-icon="
167
- copyState === 'success' ? 'mdi-check'
168
- : copyState === 'error' ? 'mdi-alert-circle'
169
- : 'mdi-content-copy'
170
- "
171
- @click="copyToClipboard"
172
- >
173
- {{
174
- copyState === "success" ? $t("core.dataExporter.copied")
175
- : copyState === "error" ? $t("core.dataExporter.copyFailed")
176
- : $t("core.dataExporter.copy")
177
- }}
178
- </v-btn>
179
- <v-btn
180
- :disabled="!serializeResult.ok"
181
- color="primary"
182
- variant="flat"
183
- size="small"
184
- class="tw:flex-1 tw:font-medium"
185
- prepend-icon="mdi-download"
186
- @click="downloadFile"
187
- >
188
- {{ $t("core.dataExporter.download") }}
189
- </v-btn>
190
- </div>
167
+ <slot name="actions" v-bind="actionsSlotProps">
168
+ <div class="tw:flex tw:gap-2 tw:mt-3">
169
+ <v-btn
170
+ v-if="clipboardSupported"
171
+ :disabled="!serializeResult.ok"
172
+ :color="copyState === 'success' ? 'success' : copyState === 'error' ? 'error' : 'primary'"
173
+ variant="tonal"
174
+ size="small"
175
+ class="tw:flex-1 tw:font-medium"
176
+ :prepend-icon="
177
+ copyState === 'success' ? 'mdi-check'
178
+ : copyState === 'error' ? 'mdi-alert-circle'
179
+ : 'mdi-content-copy'
180
+ "
181
+ @click="copyToClipboard"
182
+ >
183
+ {{
184
+ copyState === "success" ? $t("core.dataExporter.copied")
185
+ : copyState === "error" ? $t("core.dataExporter.copyFailed")
186
+ : $t("core.dataExporter.copy")
187
+ }}
188
+ </v-btn>
189
+ <v-btn
190
+ :disabled="!serializeResult.ok"
191
+ color="primary"
192
+ variant="flat"
193
+ size="small"
194
+ class="tw:flex-1 tw:font-medium"
195
+ prepend-icon="mdi-download"
196
+ @click="downloadFile"
197
+ >
198
+ {{ $t("core.dataExporter.download") }}
199
+ </v-btn>
200
+ </div>
201
+ </slot>
191
202
  </div>
192
203
  </v-card>
193
204
  </v-menu>
@@ -286,6 +297,45 @@ const triggerSlotProps = computed<TriggerSlotProps>(() => ({
286
297
  disabled: props.disabled,
287
298
  }))
288
299
 
300
+ const headerSlotProps = computed(() => ({
301
+ isOpen: isOpen.value,
302
+ close: () => {
303
+ isOpen.value = false
304
+ },
305
+ }))
306
+
307
+ const formatSelectorSlotProps = computed(() => ({
308
+ formats: resolvedFormats.value,
309
+ activeFormatId: activeFormatId.value,
310
+ }))
311
+
312
+ const scopeSwitchSlotProps = computed(() => ({
313
+ selectedScope: selectedScope.value,
314
+ showScopeSwitch: showScopeSwitch.value,
315
+ }))
316
+
317
+ const previewToggleSlotProps = computed(() => ({
318
+ previewOpen: previewOpen.value,
319
+ togglePreview: () => {
320
+ previewOpen.value = !previewOpen.value
321
+ },
322
+ }))
323
+
324
+ const previewContentSlotProps = computed(() => ({
325
+ serializeResult: serializeResult.value,
326
+ previewLines: previewLines.value,
327
+ activeFormatId: activeFormatId.value,
328
+ lineLimit: PREVIEW_LINE_LIMIT,
329
+ }))
330
+
331
+ const actionsSlotProps = computed(() => ({
332
+ clipboardSupported: clipboardSupported.value,
333
+ serializeResult: serializeResult.value,
334
+ copyState: copyState.value,
335
+ copyToClipboard,
336
+ downloadFile,
337
+ }))
338
+
289
339
  // ─── Preview lines ────────────────────────────────────────────────────────────
290
340
 
291
341
  const PREVIEW_LINE_LIMIT = 200