Skip to content

Commit 9820d63

Browse files
scottoharaantfu
andauthored
feat: present meta.defaultOptions in the config inspector (#110)
Co-authored-by: Anthony Fu <[email protected]>
1 parent ab526d0 commit 9820d63

File tree

12 files changed

+505
-258
lines changed

12 files changed

+505
-258
lines changed

app/components/RuleItem.vue

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type { RuleConfigStates, RuleInfo, RuleLevel } from '~~/shared/types'
33
import { useClipboard } from '@vueuse/core'
44
import { getRuleLevel, getRuleOptions } from '~~/shared/rules'
55
import { vTooltip } from 'floating-vue'
6+
import { deepCompareOptions } from '~/composables/options'
7+
import { getRuleDefaultOptions } from '~/composables/payload'
68
79
const props = defineProps<{
810
rule: RuleInfo
@@ -17,6 +19,11 @@ const emit = defineEmits<{
1719
stateClick: [RuleLevel]
1820
}>()
1921
22+
function redundantOptions(options: any) {
23+
const { hasRedundantOptions } = deepCompareOptions(options ?? [], getRuleDefaultOptions(props.rule.name))
24+
return hasRedundantOptions
25+
}
26+
2027
const { copy } = useClipboard()
2128
2229
function capitalize(str?: string) {
@@ -38,6 +45,7 @@ function capitalize(str?: string) {
3845
:level="s.level"
3946
:config-index="s.configIndex"
4047
:has-options="!!s.options?.length"
48+
:has-redundant-options="redundantOptions(s.options)"
4149
/>
4250
<template #popper="{ shown }">
4351
<RuleStateItem v-if="shown" :state="s" />
@@ -53,6 +61,7 @@ function capitalize(str?: string) {
5361
<RuleLevelIcon
5462
:level="getRuleLevel(value)!"
5563
:has-options="!!getRuleOptions(value)?.length"
64+
:has-redundant-options="redundantOptions(getRuleOptions(value))"
5665
/>
5766
</div>
5867

app/components/RuleLevelIcon.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { nth } from '~/composables/strings'
66
const props = defineProps<{
77
level: RuleLevel
88
hasOptions?: boolean
9+
hasRedundantOptions?: boolean
910
configIndex?: number
1011
class?: string
1112
}>()
@@ -32,6 +33,6 @@ const icon = computed(() => ({
3233
<template>
3334
<div relative :class="[color, props.class]" :title="title">
3435
<div :class="icon" />
35-
<div v-if="hasOptions" absolute right--2px top--2px h-6px w-6px rounded-full bg-current op75 />
36+
<div v-if="hasOptions" absolute right--2px top--2px h-6px w-6px rounded-full bg-current op75 :class="hasRedundantOptions ? 'text-blue5' : ''" />
3637
</div>
3738
</template>

app/components/RuleStateItem.vue

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
<script setup lang="ts">
22
import type { RuleConfigState } from '~~/shared/types'
33
import { useRouter } from '#app/composables/router'
4-
import { computed } from 'vue'
5-
import { payload } from '~/composables/payload'
4+
import { computed, reactive } from 'vue'
5+
import { deepCompareOptions } from '~/composables/options'
6+
import { getRuleDefaultOptions, payload } from '~/composables/payload'
67
import { filtersConfigs } from '~/composables/state'
7-
import { nth, stringifyUnquoted } from '~/composables/strings'
8+
import { nth, stringifyOptions } from '~/composables/strings'
89
910
const props = defineProps<{
1011
state: RuleConfigState
@@ -19,6 +20,16 @@ const colors = {
1920
2021
const config = computed(() => payload.value.configs[props.state.configIndex])
2122
23+
const defaultOptions = computed(() => getRuleDefaultOptions(props.state.name))
24+
25+
const comparedOptions = computed(() => deepCompareOptions(props.state.options ?? [], defaultOptions.value))
26+
27+
const initialRuleOptionsView = computed(() => !props.state.options?.length && defaultOptions.value?.length ? 'default' : 'state')
28+
29+
const ruleOptions = reactive({
30+
viewType: initialRuleOptionsView.value as 'state' | 'default',
31+
})
32+
2233
const router = useRouter()
2334
function goto() {
2435
filtersConfigs.rule = props.state.name
@@ -71,20 +82,52 @@ function goto() {
7182
</div>
7283
</template>
7384
</div>
74-
<template v-if="state.options?.length">
75-
<div flex="~ gap-2 items-center">
76-
<div i-ph-sliders-duotone my1 flex-none op75 />
77-
<div op50>
78-
Rule options
85+
<template v-if="state.options?.length || defaultOptions?.length">
86+
<div items-center justify-between md:flex>
87+
<div flex="~ gap-1" op50>
88+
<button
89+
v-if="state.options?.length"
90+
btn-action
91+
:class="{ 'btn-action-active': ruleOptions.viewType === 'state' }"
92+
@click="ruleOptions.viewType = 'state'"
93+
>
94+
<div i-ph-sliders-duotone my1 flex-none op75 />
95+
Rule options
96+
</button>
97+
<button
98+
v-if="defaultOptions?.length"
99+
btn-action
100+
:class="{ 'btn-action-active': ruleOptions.viewType === 'default' }"
101+
@click="ruleOptions.viewType = 'default'"
102+
>
103+
<div i-ph-faders-duotone my1 flex-none op75 />
104+
Option defaults
105+
</button>
79106
</div>
80107
</div>
81-
<Shiki
82-
v-for="options, idx of state.options"
83-
:key="idx"
84-
lang="ts"
85-
:code="stringifyUnquoted(options)"
86-
rounded bg-code p2 text-sm
87-
/>
108+
<template v-if="ruleOptions.viewType === 'state'">
109+
<Shiki
110+
v-for="options, idx of comparedOptions.options"
111+
:key="idx"
112+
lang="ts"
113+
:code="stringifyOptions(options)"
114+
rounded bg-code p2 text-sm
115+
/>
116+
</template>
117+
<template v-if="ruleOptions.viewType === 'default'">
118+
<Shiki
119+
v-for="options, idx of defaultOptions"
120+
:key="idx"
121+
lang="ts"
122+
:code="stringifyOptions(options)"
123+
rounded bg-code p2 text-sm
124+
/>
125+
</template>
126+
</template>
127+
<template v-if="ruleOptions.viewType === 'state' && comparedOptions.hasRedundantOptions">
128+
<div op50>
129+
Options <span italic op75>italicized</span> match the default for the rule
130+
</div>
88131
</template>
89132
</div>
90133
</template>

app/components/Shiki.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { transformerNotationMap } from '@shikijs/transformers'
2+
13
// @unocss-include
24
export default defineComponent({
35
name: 'Shiki',
@@ -24,6 +26,14 @@ export default defineComponent({
2426
node.properties.style = ''
2527
},
2628
},
29+
transformerNotationMap(
30+
{
31+
classMap: {
32+
muted: 'muted',
33+
},
34+
},
35+
'@shikijs/transformers:notation-muted',
36+
),
2737
],
2838
})
2939
})

app/composables/options.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Indicates if any user-supplied option values match the default value for that option
3+
*/
4+
let hasRedundantOptions: boolean
5+
6+
/**
7+
* Wraps an option value in a 3-part array, while still preserving the original data type and value.
8+
*
9+
* The '--' markers provide something that a regex replace can easily later match on.
10+
* (See transformDiff() in ./strings.ts)
11+
*/
12+
function redundantOption(option: any) {
13+
hasRedundantOptions = true
14+
return ['--', option, '--']
15+
}
16+
17+
function deepCompareOption(option: any, defaultOption: any) {
18+
if (defaultOption === void 0)
19+
return option
20+
if (typeof option !== typeof defaultOption)
21+
return option
22+
23+
if (option === defaultOption)
24+
return redundantOption(option)
25+
26+
if (typeof option === 'object' && option !== null && defaultOption !== null) {
27+
if (Array.isArray(option) && Array.isArray(defaultOption) && option.length === defaultOption.length) {
28+
if (option.length === 0)
29+
return redundantOption(option)
30+
return option.map((value: any, index: number): any[] => deepCompareOption(value, defaultOption[index]))
31+
}
32+
else if (!Array.isArray(option) && !Array.isArray(defaultOption)) {
33+
const optionKeys = Object.keys(option)
34+
35+
return optionKeys.reduce((comparedKeys: Record<string, any>, key) => {
36+
comparedKeys[key] = deepCompareOption(option[key], defaultOption[key])
37+
return comparedKeys
38+
}, {})
39+
}
40+
}
41+
42+
return option
43+
}
44+
45+
export function deepCompareOptions(options: any[], defaultOptions: any[]) {
46+
hasRedundantOptions = false
47+
const comparedOptions = options.map((value, index) => deepCompareOption(value, index < defaultOptions.length ? defaultOptions[index] : void 0))
48+
49+
return {
50+
options: comparedOptions,
51+
hasRedundantOptions,
52+
}
53+
}

app/composables/payload.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ export function getRuleFromName(name: string): RuleInfo {
9393
}
9494
}
9595

96+
export function getRuleDefaultOptions(name: string): any[] {
97+
return payload.value.rules[name]?.defaultOptions ?? []
98+
}
99+
96100
export function getRuleStates(name: string): RuleConfigStates | undefined {
97101
return payload.value.ruleToState.get(name)
98102
}

app/composables/strings.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,21 @@ export function nth(n: number) {
99
return `${n}th`
1010
}
1111

12+
export function stringifyOptions(object: any) {
13+
/**
14+
* Replaces all occurrences of the pattern:
15+
* `['--', value, '--']`
16+
*
17+
* with:
18+
* `value, // [!code muted]
19+
*
20+
* Lines with the [!code muted] comment will be processed by Shiki's diff
21+
* notation transformer and have the `.line.muted` classes applied
22+
*/
23+
return stringifyUnquoted(object)
24+
.replace(/\[\s*'--',\s*(\S.+),\s*'--'\s*\],?/g, '$1, // [!code muted]')
25+
}
26+
1227
export function stringifyUnquoted(obj: any) {
1328
return JSON.stringify(obj, null, 2)
1429
.replace(/"(\w+)":/g, '$1:')

app/styles/global.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ html.dark {
2020
font-size: 15px;
2121
}
2222

23+
.shiki span.line.muted {
24+
font-style: italic;
25+
opacity: 75%;
26+
}
27+
2328
.font-mono, [font-mono=""] {
2429
font-variant-ligatures: none;
2530
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"@iconify-json/twemoji": "catalog:",
6262
"@iconify-json/vscode-icons": "catalog:",
6363
"@nuxt/eslint": "catalog:",
64+
"@shikijs/transformers": "catalog:",
6465
"@types/connect": "catalog:",
6566
"@types/ws": "catalog:",
6667
"@typescript-eslint/utils": "catalog:",

0 commit comments

Comments
 (0)