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
2 changes: 1 addition & 1 deletion .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
},
"globals": {
"defineProps": "readonly",
"defineEmits": "readonly"
"defineModel": "readonly"
},
"rules": {
"id-length": "off",
Expand Down
2 changes: 2 additions & 0 deletions .vitepress/theme/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { h } from 'vue';
import './variables.css';
import './main.css';
import 'virtual:group-icons.css';
import ApiOutline from '../../components/ApiOutline.vue';
import CarbonAds from '../../components/CarbonAds.vue';
import ModuleIndex from '../../components/ModuleIndex.vue';
import StatusContent from '../../components/StatusContent.vue';
Expand All @@ -22,6 +23,7 @@ export default {
app.component('ModuleIndex', ModuleIndex);
app.component('StatusContent', StatusContent);
app.component('TesterContent', TesterContent);
app.component('ApiOutline', ApiOutline);

if (typeof window !== 'undefined') {
router.onBeforeRouteChange = (to) => {
Expand Down
81 changes: 81 additions & 0 deletions components/ApiOutline.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<script setup>
import { useMutationObserver } from '@vueuse/core';
import { onMounted, ref } from 'vue';

const outline = ref(null);


const onActiveChange = (mutations) => {
for (const { target, type, attributeName } of mutations) {
if (
type === 'attributes' &&
attributeName === 'class' &&
target.classList.contains('active') &&
target.classList.contains('outline-link')
) {
target.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
};


onMounted(() => {
outline.value = document.querySelector('.VPDocAsideOutline');
});


useMutationObserver(outline, onActiveChange, {
attributeFilter: ['class'],
subtree: true,
});
</script>

<template>
<span class="api-outline-marker" />
</template>

<style>
.api-outline-marker {
display: none;
}

.VPDoc:has(.api-outline-marker) .VPDocAsideOutline .outline-link + ul {
display: none;
}

.VPDoc:has(.api-outline-marker) .VPDocAsideOutline li:has(.outline-link.active) > ul,
.VPDoc:has(.api-outline-marker) .VPDocAsideOutline li.expanded > ul {
display: block;
}

.VPDoc:has(.api-outline-marker) .VPDocAsideOutline .outline-link {
display: flex;
line-height: 28px;
position: relative;
}

.VPDoc:has(.api-outline-marker) .VPDocAsideOutline .outline-link::before {
content: '';
width: 15px;
flex-shrink: 0;
}

.VPDoc:has(.api-outline-marker) .VPDocAsideOutline li:has(> ul) > .outline-link::before {
width: 15px;
height: 30px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 20 20' fill='none' stroke='%230080ff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='8 4 14 10 8 16'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: center;
cursor: pointer;
transition: transform 0.3s ease-in-out;
}

.VPDoc:has(.api-outline-marker) .VPDocAsideOutline li:has(> ul) > .outline-link {
color: var(--vp-c-brand-1);
}

.VPDoc:has(.api-outline-marker) .VPDocAsideOutline li:has(.outline-link.active) > .outline-link::before,
.VPDoc:has(.api-outline-marker) .VPDocAsideOutline li.expanded > .outline-link::before {
transform: rotate(90deg);
}
</style>
26 changes: 11 additions & 15 deletions components/CodeMirrorEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ import { EditorView, basicSetup } from 'codemirror';
import { useData } from 'vitepress';
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';

const { errorLines, language, modelValue, readOnly } = defineProps({
const { errorLines, language, readOnly } = defineProps({
errorLines: { default: () => [], type: Array },
language: { default: 'javascript', type: String },
modelValue: { default: '', type: String },
readOnly: { default: false, type: Boolean },
});


const emit = defineEmits(['update:modelValue']);
const data = defineModel({ default: '', type: String });


const { isDark } = useData();
Expand Down Expand Up @@ -81,7 +80,7 @@ onMounted(() => {
themeCompartment.of(getThemeExtension()),
EditorView.updateListener.of((update) => {
if (update.docChanged) {
emit('update:modelValue', update.state.doc.toString());
data.value = update.state.doc.toString();
}
}),
];
Expand All @@ -97,7 +96,7 @@ onMounted(() => {

view = new EditorView({
parent: editorContainer.value,
state: EditorState.create({ doc: modelValue, extensions }),
state: EditorState.create({ doc: data.value, extensions }),
});

if (readOnly && errorLines.length > 0) {
Expand All @@ -106,16 +105,13 @@ onMounted(() => {
});


watch(
() => modelValue,
(newValue) => {
if (view && newValue !== view.state.doc.toString()) {
view.dispatch({
changes: { from: 0, insert: newValue, to: view.state.doc.length },
});
}
},
);
watch(data, (newValue) => {
if (view && newValue !== view.state.doc.toString()) {
view.dispatch({
changes: { from: 0, insert: newValue, to: view.state.doc.length },
});
}
});


watch(
Expand Down
45 changes: 5 additions & 40 deletions components/TesterContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
</template>

<script setup>
import { useClipboard, useStorage } from '@vueuse/core';
import { useRoute } from 'vitepress';
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';

Expand Down Expand Up @@ -97,8 +98,8 @@ const DEFAULT_VALIDATE = `//Insert data to validate here


const route = useRoute();
const schema = ref(DEFAULT_SCHEMA);
const validate = ref(DEFAULT_VALIDATE);
const schema = useStorage('joi-sandbox-schema', DEFAULT_SCHEMA);
const validate = useStorage('joi-sandbox-validate', DEFAULT_VALIDATE);
const result = ref('');
const resultEditorContent = ref('');
const errorDetails = ref([]);
Expand All @@ -109,7 +110,7 @@ const version = ref('');
const joiInstance = ref(null);
const isLoading = ref(false);
const latestVersion = ref(joiInfo.versionsArray[0]);
const isCopied = ref(false);
const { copy, copied: isCopied } = useClipboard();
const showResetConfirm = ref(false);


Expand Down Expand Up @@ -176,33 +177,12 @@ onMounted(async () => {
validate.value = state.data;
}
} catch {}
} else {
// Check if we have saved state in localStorage
const savedSchema = localStorage.getItem('joi-sandbox-schema');
const savedValidate = localStorage.getItem('joi-sandbox-validate');
if (savedSchema !== null) {
schema.value = savedSchema;
}
if (savedValidate !== null) {
validate.value = savedValidate;
}
}

await loadJoi(v);
});


onBeforeUnmount(() => {
saveState();
});


const saveState = () => {
localStorage.setItem('joi-sandbox-schema', schema.value);
localStorage.setItem('joi-sandbox-validate', validate.value);
};


watch(
() => route.path,
async (newPath) => {
Expand All @@ -211,7 +191,6 @@ watch(
if (testerMatch) {
const v = testerMatch.at(1);
if (v !== version.value) {
saveState();
await loadJoi(v);
}
}
Expand All @@ -227,10 +206,6 @@ const onValidateClick = () => {
}


// Save to localStorage whenever we validate
saveState();


try {
// oxlint-disable-next-line no-new-func
const dataToValidate = new Function(`"use strict";return (${validate.value})`)();
Expand Down Expand Up @@ -291,8 +266,6 @@ const cancelReset = () => {

const confirmReset = () => {
showResetConfirm.value = false;
localStorage.removeItem('joi-sandbox-schema');
localStorage.removeItem('joi-sandbox-validate');


schema.value = DEFAULT_SCHEMA;
Expand All @@ -307,10 +280,6 @@ const confirmReset = () => {


const onShareClick = async () => {
// Also save to localStorage when sharing
saveState();


const state = JSON.stringify({
data: validate.value,
schema: schema.value,
Expand All @@ -321,11 +290,7 @@ const onShareClick = async () => {
const encoded = btoa(unescape(encodeURIComponent(state)));
const url = new URL(window.location.href);
url.hash = `share=${encoded}`;
await navigator.clipboard.writeText(url.toString());
isCopied.value = true;
setTimeout(() => {
isCopied.value = false;
}, 2000);
await copy(url.toString());
} catch {}
};
</script>
Expand Down
2 changes: 2 additions & 0 deletions docs/api/[version].md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
title: API
---

<ApiOutline />

# API v{{ $params.fullVersion }}

### Installation
Expand Down
2 changes: 2 additions & 0 deletions docs/module/[name]/api/[version].md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
title: Module API
---

<ApiOutline />

# {{ $params.package }} API v{{ $params.fullVersion }}

::: code-group
Expand Down
2 changes: 2 additions & 0 deletions docs/module/[name]/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
title: Module changelog
---

<ApiOutline />

# Changelog

::: v-pre
Expand Down
2 changes: 2 additions & 0 deletions docs/resources/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
title: Changelog
---

<ApiOutline />

# Changelog

::: v-pre
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@lezer/highlight": "^1.2.3",
"@uiw/codemirror-theme-darcula": "^4.25.8",
"@uiw/codemirror-theme-eclipse": "^4.25.8",
"@vueuse/core": "^14.2.1",
"codemirror": "^6.0.2",
"es-toolkit": "^1.45.1",
"joi-17": "npm:joi@17.13.3",
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.