Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/build-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,17 @@ concurrency:
cancel-in-progress: true

jobs:
format:
name: Format
runs-on: ubuntu-24.04
steps:
- uses: Perdolique/automations/.github/actions/setup-pnpm@v3
with:
install-dependencies: true
- run: pnpm run format:check

deploy:
needs: format
uses: Perdolique/automations/.github/workflows/deploy.yml@v3

with:
Expand Down
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ run_quietly() {
return "$status"
}

run_quietly 'Format check' vp run format:check
run_quietly 'Markdown lint' vp run lint:markdown
run_quietly 'Oxlint' vp run lint:oxlint:agent
run_quietly 'Typecheck' vp run test:typecheck
Expand Down
2 changes: 1 addition & 1 deletion .node-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v24.14
v26.7
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

### TypeScript or Vue file changes

- Run `vp run format` and wait for it to finish before starting the remaining checks.
- `vp run test:typecheck`
- `vp run test:unit:agent`
- `vp run lint:oxlint:agent`
Expand Down
1 change: 1 addition & 0 deletions app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
name: 'color-scheme',
content: 'light dark'
}],

link: [{
rel: 'icon',
type: 'image/png',
Expand Down
11 changes: 11 additions & 0 deletions app/assets/styles/reset.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ select {
font: inherit;
}

/* Remove default fieldset styles */
fieldset {
min-inline-size: 0;
padding: 0;
border: 0;
}

legend {
padding: 0;
}

/* Avoid text overflows */
p,
h1,
Expand Down
1 change: 0 additions & 1 deletion app/components/ActionPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@

const props = defineProps<Props>()
const slots = useSlots()

const hasMediaSlot = computed(() => slots.media !== undefined)
const showIcon = computed(() => props.icon !== undefined && props.icon !== '')
const showMedia = computed(() => hasMediaSlot.value || showIcon.value)
Expand Down
1 change: 0 additions & 1 deletion app/components/PerdHeading.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
}

const { level } = defineProps<Props>()

const tag = computed(() => `h${level}` as const)
</script>

Expand Down
1 change: 1 addition & 0 deletions app/components/PerdSearchInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
name,
placeholder
} = defineProps<Props>()

const emit = defineEmits<Emits>()

const value = defineModel<string>({
Expand Down
66 changes: 61 additions & 5 deletions app/components/TextInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,87 @@
ref="input"
v-model="value"
:class="$style.input"
:aria-describedby="describedBy"
:aria-invalid="ariaInvalid"
:autocomplete="autocomplete"
:disabled="disabled"
:maxlength="maxlength"
:name="name"
:placeholder="placeholder"
:required="required"
type="text"
autocomplete="off"
:type="type"
>

<span v-if="hasHint" :id="hintId" :class="$style.hint">
{{ hint }}
</span>

<span
v-if="hasError"
:id="errorId"
:class="$style.error"
role="alert"
>
{{ error }}
</span>
</div>
</template>

<script lang="ts" setup>
import { useId, useTemplateRef } from 'vue'
import { computed, useId, useTemplateRef } from 'vue'

interface Props {
autocomplete?: string;
disabled?: boolean;
error?: string;
hint?: string;
label: string;
maxlength?: number;
name: string;
placeholder?: string;
required?: boolean;
type?: 'text' | 'url';
}

const {
autocomplete = 'off',
disabled,
error,
hint,
label,
maxlength,
name,
placeholder,
required
required,
type = 'text'
} = defineProps<Props>()

const value = defineModel<string>({
required: true
})

const inputId = useId()
const componentId = useId()
const inputId = `${componentId}-input`
const hintId = `${componentId}-hint`
const errorId = `${componentId}-error`
const input = useTemplateRef('input')
const hasHint = computed(() => hint !== undefined)
const hasError = computed(() => error !== undefined)
const ariaInvalid = computed(() => hasError.value || undefined)

const describedBy = computed(() => {
const ids = []

if (hasHint.value) {
ids.push(hintId)
}

if (hasError.value) {
ids.push(errorId)
}

return ids.length === 0 ? undefined : ids.join(' ')
})

function focus() {
input.value?.focus()
Expand Down Expand Up @@ -88,5 +131,18 @@
outline-offset: 2px;
box-shadow: var(--shadow-focus);
}

&[aria-invalid='true'] {
border-color: var(--color-danger-primary);
}
}

.hint {
color: var(--color-text-muted);
font-size: var(--font-size-14);
}

.error {
color: var(--color-danger-primary);
}
</style>
Loading
Loading