feat: Initialize VitePress documentation system with multi-language content, custom theme, and assets.

This commit is contained in:
dyzulk
2026-01-16 15:05:45 +07:00
parent 1ae69f53f7
commit 0e38dcccc6
80 changed files with 73916 additions and 121 deletions

View File

@@ -0,0 +1,61 @@
{
"hash": "351ebb31",
"configHash": "9f61585a",
"lockfileHash": "a8ce03f4",
"browserHash": "3a62ec11",
"optimized": {
"vue": {
"src": "../../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "5b1d304c",
"needsInterop": false
},
"vitepress > @vue/devtools-api": {
"src": "../../../../node_modules/@vue/devtools-api/dist/index.js",
"file": "vitepress___@vue_devtools-api.js",
"fileHash": "b482c46a",
"needsInterop": false
},
"vitepress > @vueuse/core": {
"src": "../../../../node_modules/@vueuse/core/index.mjs",
"file": "vitepress___@vueuse_core.js",
"fileHash": "a5c5890d",
"needsInterop": false
},
"vitepress > @vueuse/integrations/useFocusTrap": {
"src": "../../../../node_modules/@vueuse/integrations/useFocusTrap.mjs",
"file": "vitepress___@vueuse_integrations_useFocusTrap.js",
"fileHash": "95f44bf0",
"needsInterop": false
},
"vitepress > mark.js/src/vanilla.js": {
"src": "../../../../node_modules/mark.js/src/vanilla.js",
"file": "vitepress___mark__js_src_vanilla__js.js",
"fileHash": "9dc093d4",
"needsInterop": false
},
"vitepress > minisearch": {
"src": "../../../../node_modules/minisearch/dist/es/index.js",
"file": "vitepress___minisearch.js",
"fileHash": "55d2ce5f",
"needsInterop": false
},
"lucide-vue-next": {
"src": "../../../../node_modules/lucide-vue-next/dist/esm/lucide-vue-next.js",
"file": "lucide-vue-next.js",
"fileHash": "d70b9ca5",
"needsInterop": false
}
},
"chunks": {
"chunk-2CLQ7TTZ": {
"file": "chunk-2CLQ7TTZ.js"
},
"chunk-LE5NDSFD": {
"file": "chunk-LE5NDSFD.js"
},
"chunk-PZ5AY32C": {
"file": "chunk-PZ5AY32C.js"
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
export {
__export
};
//# sourceMappingURL=chunk-PZ5AY32C.js.map

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,584 @@
import {
DefaultMagicKeysAliasMap,
StorageSerializers,
TransitionPresets,
assert,
breakpointsAntDesign,
breakpointsBootstrapV5,
breakpointsElement,
breakpointsMasterCss,
breakpointsPrimeFlex,
breakpointsQuasar,
breakpointsSematic,
breakpointsTailwind,
breakpointsVuetify,
breakpointsVuetifyV2,
breakpointsVuetifyV3,
bypassFilter,
camelize,
clamp,
cloneFnJSON,
computedAsync,
computedEager,
computedInject,
computedWithControl,
containsProp,
controlledRef,
createEventHook,
createFetch,
createFilterWrapper,
createGlobalState,
createInjectionState,
createRef,
createReusableTemplate,
createSharedComposable,
createSingletonPromise,
createTemplatePromise,
createUnrefFn,
customStorageEventName,
debounceFilter,
defaultDocument,
defaultLocation,
defaultNavigator,
defaultWindow,
executeTransition,
extendRef,
formatDate,
formatTimeAgo,
get,
getLifeCycleTarget,
getSSRHandler,
hasOwn,
hyphenate,
identity,
increaseWithUnit,
injectLocal,
invoke,
isClient,
isDef,
isDefined,
isIOS,
isObject,
isWorker,
makeDestructurable,
mapGamepadToXbox360Controller,
noop,
normalizeDate,
notNullish,
now,
objectEntries,
objectOmit,
objectPick,
onClickOutside,
onElementRemoval,
onKeyDown,
onKeyPressed,
onKeyStroke,
onKeyUp,
onLongPress,
onStartTyping,
pausableFilter,
promiseTimeout,
provideLocal,
provideSSRWidth,
pxValue,
rand,
reactify,
reactifyObject,
reactiveComputed,
reactiveOmit,
reactivePick,
refAutoReset,
refDebounced,
refDefault,
refThrottled,
refWithControl,
resolveRef,
resolveUnref,
set,
setSSRHandler,
syncRef,
syncRefs,
templateRef,
throttleFilter,
timestamp,
toArray,
toReactive,
toRef,
toRefs,
toValue,
tryOnBeforeMount,
tryOnBeforeUnmount,
tryOnMounted,
tryOnScopeDispose,
tryOnUnmounted,
unrefElement,
until,
useActiveElement,
useAnimate,
useArrayDifference,
useArrayEvery,
useArrayFilter,
useArrayFind,
useArrayFindIndex,
useArrayFindLast,
useArrayIncludes,
useArrayJoin,
useArrayMap,
useArrayReduce,
useArraySome,
useArrayUnique,
useAsyncQueue,
useAsyncState,
useBase64,
useBattery,
useBluetooth,
useBreakpoints,
useBroadcastChannel,
useBrowserLocation,
useCached,
useClipboard,
useClipboardItems,
useCloned,
useColorMode,
useConfirmDialog,
useCountdown,
useCounter,
useCssVar,
useCurrentElement,
useCycleList,
useDark,
useDateFormat,
useDebounceFn,
useDebouncedRefHistory,
useDeviceMotion,
useDeviceOrientation,
useDevicePixelRatio,
useDevicesList,
useDisplayMedia,
useDocumentVisibility,
useDraggable,
useDropZone,
useElementBounding,
useElementByPoint,
useElementHover,
useElementSize,
useElementVisibility,
useEventBus,
useEventListener,
useEventSource,
useEyeDropper,
useFavicon,
useFetch,
useFileDialog,
useFileSystemAccess,
useFocus,
useFocusWithin,
useFps,
useFullscreen,
useGamepad,
useGeolocation,
useIdle,
useImage,
useInfiniteScroll,
useIntersectionObserver,
useInterval,
useIntervalFn,
useKeyModifier,
useLastChanged,
useLocalStorage,
useMagicKeys,
useManualRefHistory,
useMediaControls,
useMediaQuery,
useMemoize,
useMemory,
useMounted,
useMouse,
useMouseInElement,
useMousePressed,
useMutationObserver,
useNavigatorLanguage,
useNetwork,
useNow,
useObjectUrl,
useOffsetPagination,
useOnline,
usePageLeave,
useParallax,
useParentElement,
usePerformanceObserver,
usePermission,
usePointer,
usePointerLock,
usePointerSwipe,
usePreferredColorScheme,
usePreferredContrast,
usePreferredDark,
usePreferredLanguages,
usePreferredReducedMotion,
usePreferredReducedTransparency,
usePrevious,
useRafFn,
useRefHistory,
useResizeObserver,
useSSRWidth,
useScreenOrientation,
useScreenSafeArea,
useScriptTag,
useScroll,
useScrollLock,
useSessionStorage,
useShare,
useSorted,
useSpeechRecognition,
useSpeechSynthesis,
useStepper,
useStorage,
useStorageAsync,
useStyleTag,
useSupported,
useSwipe,
useTemplateRefsList,
useTextDirection,
useTextSelection,
useTextareaAutosize,
useThrottleFn,
useThrottledRefHistory,
useTimeAgo,
useTimeout,
useTimeoutFn,
useTimeoutPoll,
useTimestamp,
useTitle,
useToNumber,
useToString,
useToggle,
useTransition,
useUrlSearchParams,
useUserMedia,
useVModel,
useVModels,
useVibrate,
useVirtualList,
useWakeLock,
useWebNotification,
useWebSocket,
useWebWorker,
useWebWorkerFn,
useWindowFocus,
useWindowScroll,
useWindowSize,
watchArray,
watchAtMost,
watchDebounced,
watchDeep,
watchIgnorable,
watchImmediate,
watchOnce,
watchPausable,
watchThrottled,
watchTriggerable,
watchWithFilter,
whenever
} from "./chunk-2CLQ7TTZ.js";
import "./chunk-LE5NDSFD.js";
import "./chunk-PZ5AY32C.js";
export {
DefaultMagicKeysAliasMap,
StorageSerializers,
TransitionPresets,
assert,
computedAsync as asyncComputed,
refAutoReset as autoResetRef,
breakpointsAntDesign,
breakpointsBootstrapV5,
breakpointsElement,
breakpointsMasterCss,
breakpointsPrimeFlex,
breakpointsQuasar,
breakpointsSematic,
breakpointsTailwind,
breakpointsVuetify,
breakpointsVuetifyV2,
breakpointsVuetifyV3,
bypassFilter,
camelize,
clamp,
cloneFnJSON,
computedAsync,
computedEager,
computedInject,
computedWithControl,
containsProp,
computedWithControl as controlledComputed,
controlledRef,
createEventHook,
createFetch,
createFilterWrapper,
createGlobalState,
createInjectionState,
reactify as createReactiveFn,
createRef,
createReusableTemplate,
createSharedComposable,
createSingletonPromise,
createTemplatePromise,
createUnrefFn,
customStorageEventName,
debounceFilter,
refDebounced as debouncedRef,
watchDebounced as debouncedWatch,
defaultDocument,
defaultLocation,
defaultNavigator,
defaultWindow,
computedEager as eagerComputed,
executeTransition,
extendRef,
formatDate,
formatTimeAgo,
get,
getLifeCycleTarget,
getSSRHandler,
hasOwn,
hyphenate,
identity,
watchIgnorable as ignorableWatch,
increaseWithUnit,
injectLocal,
invoke,
isClient,
isDef,
isDefined,
isIOS,
isObject,
isWorker,
makeDestructurable,
mapGamepadToXbox360Controller,
noop,
normalizeDate,
notNullish,
now,
objectEntries,
objectOmit,
objectPick,
onClickOutside,
onElementRemoval,
onKeyDown,
onKeyPressed,
onKeyStroke,
onKeyUp,
onLongPress,
onStartTyping,
pausableFilter,
watchPausable as pausableWatch,
promiseTimeout,
provideLocal,
provideSSRWidth,
pxValue,
rand,
reactify,
reactifyObject,
reactiveComputed,
reactiveOmit,
reactivePick,
refAutoReset,
refDebounced,
refDefault,
refThrottled,
refWithControl,
resolveRef,
resolveUnref,
set,
setSSRHandler,
syncRef,
syncRefs,
templateRef,
throttleFilter,
refThrottled as throttledRef,
watchThrottled as throttledWatch,
timestamp,
toArray,
toReactive,
toRef,
toRefs,
toValue,
tryOnBeforeMount,
tryOnBeforeUnmount,
tryOnMounted,
tryOnScopeDispose,
tryOnUnmounted,
unrefElement,
until,
useActiveElement,
useAnimate,
useArrayDifference,
useArrayEvery,
useArrayFilter,
useArrayFind,
useArrayFindIndex,
useArrayFindLast,
useArrayIncludes,
useArrayJoin,
useArrayMap,
useArrayReduce,
useArraySome,
useArrayUnique,
useAsyncQueue,
useAsyncState,
useBase64,
useBattery,
useBluetooth,
useBreakpoints,
useBroadcastChannel,
useBrowserLocation,
useCached,
useClipboard,
useClipboardItems,
useCloned,
useColorMode,
useConfirmDialog,
useCountdown,
useCounter,
useCssVar,
useCurrentElement,
useCycleList,
useDark,
useDateFormat,
refDebounced as useDebounce,
useDebounceFn,
useDebouncedRefHistory,
useDeviceMotion,
useDeviceOrientation,
useDevicePixelRatio,
useDevicesList,
useDisplayMedia,
useDocumentVisibility,
useDraggable,
useDropZone,
useElementBounding,
useElementByPoint,
useElementHover,
useElementSize,
useElementVisibility,
useEventBus,
useEventListener,
useEventSource,
useEyeDropper,
useFavicon,
useFetch,
useFileDialog,
useFileSystemAccess,
useFocus,
useFocusWithin,
useFps,
useFullscreen,
useGamepad,
useGeolocation,
useIdle,
useImage,
useInfiniteScroll,
useIntersectionObserver,
useInterval,
useIntervalFn,
useKeyModifier,
useLastChanged,
useLocalStorage,
useMagicKeys,
useManualRefHistory,
useMediaControls,
useMediaQuery,
useMemoize,
useMemory,
useMounted,
useMouse,
useMouseInElement,
useMousePressed,
useMutationObserver,
useNavigatorLanguage,
useNetwork,
useNow,
useObjectUrl,
useOffsetPagination,
useOnline,
usePageLeave,
useParallax,
useParentElement,
usePerformanceObserver,
usePermission,
usePointer,
usePointerLock,
usePointerSwipe,
usePreferredColorScheme,
usePreferredContrast,
usePreferredDark,
usePreferredLanguages,
usePreferredReducedMotion,
usePreferredReducedTransparency,
usePrevious,
useRafFn,
useRefHistory,
useResizeObserver,
useSSRWidth,
useScreenOrientation,
useScreenSafeArea,
useScriptTag,
useScroll,
useScrollLock,
useSessionStorage,
useShare,
useSorted,
useSpeechRecognition,
useSpeechSynthesis,
useStepper,
useStorage,
useStorageAsync,
useStyleTag,
useSupported,
useSwipe,
useTemplateRefsList,
useTextDirection,
useTextSelection,
useTextareaAutosize,
refThrottled as useThrottle,
useThrottleFn,
useThrottledRefHistory,
useTimeAgo,
useTimeout,
useTimeoutFn,
useTimeoutPoll,
useTimestamp,
useTitle,
useToNumber,
useToString,
useToggle,
useTransition,
useUrlSearchParams,
useUserMedia,
useVModel,
useVModels,
useVibrate,
useVirtualList,
useWakeLock,
useWebNotification,
useWebSocket,
useWebWorker,
useWebWorkerFn,
useWindowFocus,
useWindowScroll,
useWindowSize,
watchArray,
watchAtMost,
watchDebounced,
watchDeep,
watchIgnorable,
watchImmediate,
watchOnce,
watchPausable,
watchThrottled,
watchTriggerable,
watchWithFilter,
whenever
};
//# sourceMappingURL=vitepress___@vueuse_core.js.map

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

348
docs/.vitepress/cache/deps/vue.js vendored Normal file
View File

@@ -0,0 +1,348 @@
import {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBaseVNode,
createBlock,
createCommentVNode,
createElementBlock,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
nodeOps,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
patchProp,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
} from "./chunk-LE5NDSFD.js";
import "./chunk-PZ5AY32C.js";
export {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBlock,
createCommentVNode,
createElementBlock,
createBaseVNode as createElementVNode,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
nodeOps,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
patchProp,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
};
//# sourceMappingURL=vue.js.map

7
docs/.vitepress/cache/deps/vue.js.map vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}

View File

@@ -1,4 +1,6 @@
import { defineConfig } from 'vitepress'
import { sidebarEn, sidebarId } from './config/sidebars'
import { navEn, navId } from './config/nav'
export default defineConfig({
title: "MIVO",
@@ -8,45 +10,17 @@ export default defineConfig({
lastUpdated: true,
head: [
['link', { rel: 'icon', href: '/logo.png' }]
['link', { rel: 'icon', href: '/logo-m.svg' }]
],
// Shared theme config
themeConfig: {
logo: '/logo.png',
logo: {
light: '/logo-m.svg',
dark: '/logo-m-dark.svg'
},
siteTitle: 'MIVO',
nav: [
{ text: 'Home', link: '/' },
{ text: 'Guide', link: '/guide/installation' },
{ text: 'Docker', link: '/guide/docker' },
{ text: 'GitHub', link: 'https://github.com/dyzulk/mivo' }
],
sidebar: [
{
text: 'Introduction',
items: [
{ text: 'What is MIVO?', link: '/' },
{ text: 'Installation', link: '/guide/installation' }
]
},
{
text: 'Deployment',
items: [
{ text: 'Docker Guide', link: '/guide/docker' },
{ text: 'Manual Installation', link: '/guide/installation#manual-installation' },
{ text: 'PaaS / Cloud', link: '/guide/installation#paas-cloud-railway-render-heroku' }
]
},
{
text: 'Support',
items: [
{ text: 'Contribution', link: 'https://github.com/dyzulk/mivo/blob/main/CONTRIBUTING.md' },
{ text: 'Donate', link: 'https://sociabuzz.com/dyzulkdev/tribe' }
]
}
],
socialLinks: [
{ icon: 'github', link: 'https://github.com/dyzulk/mivo' }
],
@@ -59,5 +33,25 @@ export default defineConfig({
search: {
provider: 'local'
}
},
locales: {
root: {
label: 'English',
lang: 'en',
themeConfig: {
nav: navEn,
sidebar: sidebarEn
}
},
id: {
label: 'Indonesia',
lang: 'id',
themeConfig: {
nav: navId,
sidebar: sidebarId
}
}
}
})

View File

@@ -0,0 +1,27 @@
import { DefaultTheme } from 'vitepress'
export const navEn: DefaultTheme.NavItem[] = [
{ text: 'Home', link: '/' },
{ text: 'Guide', link: '/guide/installation' },
{ text: 'Manual', link: '/manual/' },
{
text: 'Community',
items: [
{ text: 'Changelog', link: 'https://github.com/dyzulk/mivo/releases' },
{ text: 'Contributing', link: 'https://github.com/dyzulk/mivo/blob/main/CONTRIBUTING.md' }
]
}
]
export const navId: DefaultTheme.NavItem[] = [
{ text: 'Beranda', link: '/id/' },
{ text: 'Panduan', link: '/id/guide/installation' },
{ text: 'Buku Manual', link: '/id/manual/' },
{
text: 'Komunitas',
items: [
{ text: 'Catatan Rilis', link: 'https://github.com/dyzulk/mivo/releases' },
{ text: 'Kontribusi', link: 'https://github.com/dyzulk/mivo/blob/main/CONTRIBUTING.md' }
]
}
]

View File

@@ -0,0 +1,145 @@
import { DefaultTheme } from 'vitepress'
// English Sidebars
export const sidebarEn: DefaultTheme.Sidebar = {
// Sidebar for /guide/ path
'/guide/': [
{
text: 'Getting Started',
collapsed: false,
items: [
{ text: 'Introduction', link: '/guide/' },
{ text: 'Requirements', link: '/guide/installation#requirements' }
]
},
{
text: 'Installation',
collapsed: false,
items: [
{ text: 'Docker', link: '/guide/docker' },
{ text: 'Web Server', link: '/guide/installation#web-servers' },
{ text: 'Shared Hosting', link: '/guide/installation#shared-hosting' },
{ text: 'VPS & Cloud', link: '/guide/installation#vps-cloud' },
{ text: 'Mobile & STB', link: '/guide/installation#mobile-stb' }
]
},
{
text: 'Configuration',
items: [
{ text: 'Post-Installation', link: '/guide/installation#post-installation' }
]
},
{
text: 'Support',
items: [
{ text: 'Contribution', link: 'https://github.com/dyzulk/mivo/blob/main/CONTRIBUTING.md' },
{ text: 'Donate', link: 'https://sociabuzz.com/dyzulkdev/tribe' }
]
}
],
// Sidebar for /manual/ path
'/manual/': [
{
text: 'User Manual',
items: [
{ text: 'Overview', link: '/manual/' }
]
},
{
text: 'Global Settings',
items: [
{ text: 'Introduction', link: '/manual/settings/' },
{ text: 'Routers', link: '/manual/settings/routers' },
{ text: 'Templates', link: '/manual/settings/templates' },
{ text: 'Logos', link: '/manual/settings/logos' },
{ text: 'API & CORS', link: '/manual/settings/api-cors' },
{ text: 'System', link: '/manual/settings/system' }
]
},
{
text: 'Router Operations',
items: [
{ text: 'Introduction', link: '/manual/router/' },
{ text: 'Dashboard', link: '/manual/router/dashboard' },
{ text: 'Quick Print', link: '/manual/router/quick-print' },
{ text: 'Hotspot Management', link: '/manual/router/hotspot' },
{ text: 'Reports & Logs', link: '/manual/router/reports' },
{ text: 'Network & System', link: '/manual/router/tools' }
]
}
]
}
// Indonesian Sidebars
export const sidebarId: DefaultTheme.Sidebar = {
// Sidebar for /id/guide/ path
'/id/guide/': [
{
text: 'Pengenalan',
collapsed: false,
items: [
{ text: 'Apa itu MIVO?', link: '/id/guide/' },
{ text: 'Persyaratan', link: '/id/guide/installation#persyaratan' }
]
},
{
text: 'Instalasi',
collapsed: false,
items: [
{ text: 'Docker', link: '/id/guide/docker' },
{ text: 'Web Server', link: '/id/guide/installation#web-server' },
{ text: 'Shared Hosting', link: '/id/guide/installation#shared-hosting' },
{ text: 'VPS & Cloud', link: '/id/guide/installation#vps-cloud' },
{ text: 'Mobile & STB', link: '/id/guide/installation#mobile-stb' }
]
},
{
text: 'Konfigurasi',
items: [
{ text: 'Pasca-Instalasi', link: '/id/guide/installation#pasca-instalasi' }
]
},
{
text: 'Dukungan',
items: [
{ text: 'Kontribusi', link: 'https://github.com/dyzulk/mivo/blob/main/CONTRIBUTING.md' },
{ text: 'Donasi', link: 'https://sociabuzz.com/dyzulkdev/tribe' }
]
}
],
// Sidebar for /id/manual/ path
'/id/manual/': [
{
text: 'Buku Manual',
items: [
{ text: 'Ringkasan', link: '/id/manual/' }
]
},
{
text: 'Pengaturan Global',
items: [
{ text: 'Pendahuluan', link: '/id/manual/settings/' },
{ text: 'Router', link: '/id/manual/settings/routers' },
{ text: 'Template', link: '/id/manual/settings/templates' },
{ text: 'Logo', link: '/id/manual/settings/logos' },
{ text: 'API & CORS', link: '/id/manual/settings/api-cors' },
{ text: 'Sistem', link: '/id/manual/settings/system' }
]
},
{
text: 'Operasional Router',
items: [
{ text: 'Pendahuluan', link: '/id/manual/router/' },
{ text: 'Dashboard', link: '/id/manual/router/dashboard' },
{ text: 'Cetak Cepat', link: '/id/manual/router/quick-print' },
{ text: 'Manajemen Hotspot', link: '/id/manual/router/hotspot' },
{ text: 'Laporan & Log', link: '/id/manual/router/reports' },
{ text: 'Jaringan & Sistem', link: '/id/manual/router/tools' }
]
}
]
}

View File

@@ -0,0 +1,82 @@
<script setup>
import DefaultTheme from 'vitepress/theme'
const { Layout } = DefaultTheme
</script>
<template>
<Layout>
<template #layout-bottom>
<div class="mivo-bg">
<!-- Subtle Grid Pattern -->
<div class="mivo-grid"></div>
<!-- Glowing Orbs -->
<div class="mivo-orb orb-1"></div>
<div class="mivo-orb orb-2"></div>
</div>
</template>
</Layout>
</template>
<style scoped>
.mivo-bg {
position: fixed;
inset: 0;
z-index: -1;
pointer-events: none;
overflow: hidden;
}
.mivo-grid {
position: absolute;
inset: 0;
background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGNpcmNsZSBjeD0iMSIgY3k9IjEiIHI9IjEiIGZpbGw9InJnYmEoMCwwLDAsMC4zKSIvPjwvc3ZnPg==');
mask-image: linear-gradient(to bottom, white, transparent);
-webkit-mask-image: linear-gradient(to bottom, white, transparent);
}
:root.dark .mivo-grid {
background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGNpcmNsZSBjeD0iMSIgY3k9IjEiIHI9IjEiIGZpbGw9InJnYmEoMjU1LDI1NSwyNTUsMC4wNSkiLz48L3N2Zz4=');
}
.mivo-orb {
position: absolute;
border-radius: 9999px;
filter: blur(100px);
opacity: 0.2;
animation: pulse 8s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
:root.dark .mivo-orb {
opacity: 0.05;
}
.orb-1 {
top: -20%;
left: -10%;
width: 70vw;
height: 70vw;
background-color: #3b82f6; /* blue-500 */
animation-duration: 4s;
}
.orb-2 {
top: 30%;
right: -15%;
width: 60vw;
height: 60vw;
background-color: #a855f7; /* purple-500 */
animation-duration: 6s;
animation-delay: 1s;
}
@keyframes pulse {
0%, 100% { opacity: 0.2; }
50% { opacity: 0.15; }
}
:root.dark @keyframes pulse {
0%, 100% { opacity: 0.05; }
50% { opacity: 0.03; }
}
</style>

View File

@@ -0,0 +1,60 @@
<template>
<component
:is="iconComponent"
v-if="iconComponent"
:size="size || 20"
:stroke-width="strokeWidth || 2"
class="lucide-icon"
:style="{ color: resolvedColor }"
v-bind="$attrs"
/>
</template>
<script setup>
import { computed } from 'vue'
import * as icons from 'lucide-vue-next'
const props = defineProps({
name: {
type: String,
required: true
},
size: [Number, String],
strokeWidth: [Number, String],
color: {
type: String,
default: 'base'
}
})
const semanticColors = {
base: 'var(--mivo-icon-base)',
muted: 'var(--mivo-icon-muted)',
primary: 'var(--mivo-icon-primary)',
info: 'var(--mivo-icon-info)',
success: 'var(--mivo-icon-success)',
warning: 'var(--mivo-icon-warning)',
danger: 'var(--mivo-icon-danger)'
}
const resolvedColor = computed(() => {
return semanticColors[props.color] || props.color
})
const iconComponent = computed(() => {
// Handle both PascalCase (Search) and kebab-case (search-icon)
const pascalName = props.name
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join('')
return icons[pascalName] || icons[props.name]
})
</script>
<style scoped>
.lucide-icon {
display: inline-block;
vertical-align: middle;
}
</style>

View File

@@ -0,0 +1,13 @@
import DefaultTheme from 'vitepress/theme'
import Layout from './Layout.vue'
import Icon from './components/Icon.vue'
import 'flag-icons/css/flag-icons.min.css'
import './style.css'
export default {
extends: DefaultTheme,
Layout: Layout,
enhanceApp({ app }) {
app.component('Icon', Icon)
}
}

View File

@@ -0,0 +1,429 @@
/* Fonts Setup */
@font-face {
font-family: 'Geist';
src: url('/assets/fonts/Geist-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'Geist';
src: url('/assets/fonts/Geist-Bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: 'Geist Mono';
src: url('/assets/fonts/GeistMono-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
}
:root {
/* Fonts */
--vp-font-family-base: "Geist", sans-serif;
--vp-font-family-mono: "Geist Mono", monospace;
/* Colors Override to match MIVO */
--vp-c-bg: #ffffff;
--vp-c-bg-alt: #fafafa;
--vp-c-bg-elv: #ffffff;
--vp-c-text-1: #000000;
--vp-c-text-2: #666666;
--vp-c-brand-1: #000000;
--vp-c-brand-2: #333333;
--vp-c-brand-3: #666666;
/* Icon Colors - Light */
--mivo-icon-base: var(--vp-c-text-1);
--mivo-icon-muted: var(--vp-c-text-2);
--mivo-icon-primary: var(--vp-c-brand-1);
--mivo-icon-info: #3b82f6;
--mivo-icon-success: #22c55e;
--mivo-icon-warning: #eab308;
--mivo-icon-danger: #ef4444;
}
.dark {
--vp-c-bg: #000000;
--vp-c-bg-alt: #111111;
--vp-c-bg-elv: #111111;
--vp-c-text-1: #ffffff;
--vp-c-text-2: #888888;
--vp-c-brand-1: #ffffff;
--vp-c-brand-2: #eaeaea;
--vp-c-brand-3: #999999;
/* Icon Colors - Dark */
--mivo-icon-base: var(--vp-c-text-1);
--mivo-icon-muted: var(--vp-c-text-2);
--mivo-icon-primary: var(--vp-c-brand-1);
--mivo-icon-info: #60a5fa;
--mivo-icon-success: #4ade80;
--mivo-icon-warning: #facc15;
--mivo-icon-danger: #f87171;
}
/* Glassmorphism Overrides */
/* Navbar Glass */
.VPNav {
background-color: rgba(255, 255, 255, 0.6) !important;
backdrop-filter: blur(12px);
border-bottom: 1px solid rgba(0,0,0,0.05);
}
.dark .VPNav {
background-color: rgba(0, 0, 0, 0.6) !important;
border-bottom: 1px solid rgba(255,255,255,0.1);
}
/* Sidebar Glass */
.VPSidebar {
background-color: rgba(255, 255, 255, 0.3) !important;
backdrop-filter: blur(12px);
border-right: 1px solid rgba(0,0,0,0.05);
}
.dark .VPSidebar {
background-color: rgba(0, 0, 0, 0.3) !important;
border-right: 1px solid rgba(255,255,255,0.1);
}
/* Content Container - Transparent to show particles */
.VPContent {
background: transparent !important;
}
/* Footer Glass */
.VPFooter {
background-color: transparent !important;
border-top: 1px solid rgba(0,0,0,0.05);
}
.dark .VPFooter {
border-top: 1px solid rgba(255,255,255,0.1);
}
/* Local Search Box Glass */
.VPLocalSearchBox {
background-color: rgba(255, 255, 255, 0.8) !important;
backdrop-filter: blur(12px);
}
.dark .VPLocalSearchBox {
background-color: rgba(0, 0, 0, 0.8) !important;
}
/* Glassmorphism Cards (Feature Cards) */
.VPFeature {
background-color: rgba(255, 255, 255, 0.25) !important;
backdrop-filter: blur(40px);
border: 2px solid rgba(255, 255, 255, 0.2) !important;
border-radius: 1rem !important; /* rounded-2xl */
transition: all 0.3s ease;
}
.dark .VPFeature {
background-color: rgba(0, 0, 0, 0.4) !important;
border-color: rgba(255, 255, 255, 0.1) !important;
}
.VPFeature:hover {
border-color: rgba(255, 255, 255, 0.4) !important;
background-color: rgba(255, 255, 255, 0.4) !important;
transform: translateY(-4px);
box-shadow: 0 10px 30px -10px rgba(0,0,0,0.1);
}
.dark .VPFeature:hover {
border-color: rgba(255, 255, 255, 0.2) !important;
background-color: rgba(0, 0, 0, 0.6) !important;
}
/* Fix text colors inside cards if needed */
/* Global Button Styling (MIVO Style) */
.VPButton {
border-radius: 0.375rem !important; /* rounded-md */
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
font-weight: 500 !important;
text-transform: none !important;
letter-spacing: normal !important;
}
.VPButton:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.VPButton:active {
transform: scale(0.95);
}
/* Primary Button (Brand) */
.VPButton.brand {
background-color: var(--vp-c-brand-1) !important;
border-color: var(--vp-c-brand-1) !important;
color: var(--vp-c-bg) !important;
}
.dark .VPButton.brand {
color: #000 !important; /* Ensure black text on white button in dark mode */
}
/* Secondary Button (Alt) */
.VPButton.alt {
background-color: transparent !important;
border: 1px solid var(--vp-c-brand-2) !important;
color: var(--vp-c-text-1) !important;
}
/* Mobile Navigation (Glass Overlay) */
.VPNavScreen {
background-color: rgba(255, 255, 255, 0.95) !important;
backdrop-filter: blur(20px);
position: fixed !important;
top: var(--vp-nav-height, 64px) !important;
bottom: 0 !important;
left: 0 !important;
right: 0 !important;
width: 100% !important;
height: calc(100vh - var(--vp-nav-height, 64px)) !important; /* Force Height */
z-index: 90 !important;
overflow-y: auto !important;
opacity: 1 !important; /* Always opacity 1 if visible */
pointer-events: auto !important;
/* Remove transition that might delay visibility */
transition: background-color 0.25s !important;
}
/* Rely on VitePress/Vue to toggle 'display' property */
/* We just ensure that IF it is displayed, it looks right */
.dark .VPNavScreen {
background-color: rgba(0, 0, 0, 0.95) !important;
}
/* Mobile Sidebar (Slide-out) */
@media (max-width: 960px) {
.VPSidebar {
background-color: rgba(255, 255, 255, 0.9) !important; /* Less transparent on mobile for readability */
backdrop-filter: blur(20px);
border-right: none !important;
box-shadow: 10px 0 30px rgba(0,0,0,0.1);
}
.dark .VPSidebar {
background-color: rgba(20, 20, 20, 0.9) !important;
}
}
/* Fix Hamburger Menu Icon Color & Interaction */
.VPNavBarHamburger {
cursor: pointer !important;
pointer-events: auto !important;
z-index: 100 !important;
}
.VPNavBarHamburger .container .top,
.VPNavBarHamburger .container .middle,
.VPNavBarHamburger .container .bottom {
background-color: var(--vp-c-text-1) !important;
}
/* Ensure Logo Text is Visible on Mobile */
/* High Contrast Sidebar & TOC */
/* High Contrast Sidebar & TOC */
.VPSidebarItem .text {
color: var(--vp-c-text-2) !important;
opacity: 1 !important;
font-weight: 400 !important;
}
/* Level 0 Parent Headers (Collapsible) */
.VPSidebarItem.level-0 > .item > .text,
.VPSidebarItem.level-0 > .item > .VPLink > .text {
color: var(--vp-c-text-1) !important;
font-weight: 700 !important; /* Bold for headers */
}
/* Level 1+ Sub-items explicitly regular */
.VPSidebarItem.level-1 .text,
.VPSidebarItem.level-2 .text {
font-weight: 400 !important;
}
/* Sidebar Lines (GitBook Style) */
.VPSidebarItem.level-0 > .items {
border-left: 1px solid rgba(0, 0, 0, 0.05); /* Light vertical line */
margin-left: 1.15rem;
padding-left: 0.5rem;
}
.dark .VPSidebarItem.level-0 > .items {
border-left: 1px solid rgba(255, 255, 255, 0.1);
}
/* Active Sidebar Item Differentiation */
.VPSidebarItem.is-active > .item {
border-left: 2px solid var(--vp-c-brand-1); /* Bold indicator line */
margin-left: calc(-0.5rem - 1px); /* Overlap the group line */
padding-left: calc(0.5rem - 1px);
}
.VPSidebarItem.is-active > .item > .text,
.VPSidebarItem.is-active > .item > .VPLink > .text {
color: var(--vp-c-brand-1) !important;
font-weight: 400 !important; /* Keep active sub-item thinner than collapsible header */
background-color: transparent !important; /* Clean style */
}
/* Navbar Active Underline */
.VPNavBarMenuLink.active {
color: var(--vp-c-brand-1) !important;
position: relative;
}
.VPNavBarMenuLink.active::after {
content: "";
position: absolute;
bottom: -4px;
left: 0;
width: 100%;
height: 2px;
background-color: var(--vp-c-brand-1);
}
.VPDocOutlineItem .text {
color: var(--vp-c-text-2) !important;
font-size: 0.85rem;
font-weight: 400 !important;
}
.VPDocOutlineItem.active .text {
color: var(--vp-c-brand-1) !important;
font-weight: 600 !important;
}
/* Dark Mode Specific Contrast Boost */
.dark .VPSidebarItem .text,
.dark .VPDocOutlineItem .text {
color: #b0b0b0 !important; /* Brighter than default gray */
}
.dark .VPSidebarItem.is-active > .item > .text,
.dark .VPSidebarItem.is-active > .item > .VPLink > .text,
.dark .VPDocOutlineItem.active .text,
.dark .VPSidebarItem.level-0 > .item > .text,
.dark .VPSidebarItem.level-0 > .item > .VPLink > .text {
color: #ffffff !important;
/* Font weights are already inherited or set above */
}
/* Fix Code Block Background to be Glassy too */
.vp-code-group .tabs {
background-color: rgba(255,255,255,0.5) !important;
}
.dark .vp-code-group .tabs {
background-color: rgba(0,0,0,0.5) !important;
}
/* Markdown Divider Contrast */
.vp-doc hr {
border: none;
border-top: 1px solid rgba(0, 0, 0, 0.15); /* Stronger in light mode */
margin: 3rem 0;
}
.dark .vp-doc hr {
border-top: 1px solid rgba(255, 255, 255, 0.2); /* Stronger in dark mode */
}
/* Pagination Cards */
.pager-link {
background-color: rgba(255, 255, 255, 0.4) !important;
backdrop-filter: blur(8px);
border: 1px solid rgba(0, 0, 0, 0.05) !important;
border-radius: 0.75rem !important; /* rounded-xl */
padding: 1.5rem !important;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
text-decoration: none !important;
display: flex !important;
flex-direction: column !important;
}
.dark .pager-link {
background-color: rgba(255, 255, 255, 0.03) !important;
border-color: rgba(255, 255, 255, 0.1) !important;
}
.pager-link:hover {
border-color: var(--vp-c-brand-1) !important;
transform: translateY(-4px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
background-color: rgba(255, 255, 255, 0.6) !important;
}
.dark .pager-link:hover {
background-color: rgba(255, 255, 255, 0.08) !important;
}
.pager-link .desc {
color: var(--vp-c-text-2) !important;
font-size: 0.8rem !important;
font-weight: 500 !important;
margin-bottom: 4px !important;
display: block !important;
}
.pager-link .title {
color: var(--vp-c-brand-1) !important;
font-size: 1.1rem !important;
font-weight: 700 !important;
}
.pager-link.next {
text-align: right !important;
align-items: flex-end !important;
}
/* Language Switcher Flags Integration */
.VPNavBarTranslations .items .title::before,
.VPNavBarTranslations .items .VPMenuLink .VPLink.link span::before {
content: "";
display: inline-block;
width: 1.33333333em;
height: 1em;
margin-right: 0.6rem;
vertical-align: middle;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
/* --- English Flag (US) --- */
/* 1. Target the link to English (Any link NOT starting with /id/) */
.VPNavBarTranslations a:not([href^="/id/"])::before,
.VPNavBarTranslations a:not([href^="/id/"]) span::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 480'%3E%3Cpath fill='%23bd3d44' d='M0 0h640v480H0z'/%3E%3Cpath stroke='%23fff' stroke-width='37' d='M0 74h640M0 148h640M0 222h640M0 296h640M0 370h640M0 444h640'/%3E%3Cpath fill='%23192f5d' d='M0 0h364v258.5H0z'/%3E%3Cg fill='%23fff'%3E%3Cg id='a'%3E%3Cg id='b'%3E%3Cpath id='c' d='M31 23l5 15.5-13.1-9.5H44l-13.1 9.5'/%3E%3Cuse href='%23c' x='62'/%3E%3Cuse href='%23c' x='124'/%3E%3Cuse href='%23c' x='186'/%3E%3Cuse href='%23c' x='248'/%3E%3C/g%3E%3Cuse href='%23b' x='31' y='21'/%3E%3C/g%3E%3Cuse href='%23a' y='42'/%3E%3Cuse href='%23a' y='84'/%3E%3Cuse href='%23a' y='126'/%3E%3Cuse href='%23a' y='168'/%3E%3C/g%3E%3C/svg%3E");
}
/* 2. Target the active English title (when on /) */
html[lang="en"] .VPNavBarTranslations .items .title::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 480'%3E%3Cpath fill='%23bd3d44' d='M0 0h640v480H0z'/%3E%3Cpath stroke='%23fff' stroke-width='37' d='M0 74h640M0 148h640M0 222h640M0 296h640M0 370h640M0 444h640'/%3E%3Cpath fill='%23192f5d' d='M0 0h364v258.5H0z'/%3E%3Cg fill='%23fff'%3E%3Cg id='a'%3E%3Cg id='b'%3E%3Cpath id='c' d='M31 23l5 15.5-13.1-9.5H44l-13.1 9.5'/%3E%3Cuse href='%23c' x='62'/%3E%3Cuse href='%23c' x='124'/%3E%3Cuse href='%23c' x='186'/%3E%3Cuse href='%23c' x='248'/%3E%3C/g%3E%3Cuse href='%23b' x='31' y='21'/%3E%3C/g%3E%3Cuse href='%23a' y='42'/%3E%3Cuse href='%23a' y='84'/%3E%3Cuse href='%23a' y='126'/%3E%3Cuse href='%23a' y='168'/%3E%3C/g%3E%3C/svg%3E");
}
/* --- Indonesia Flag (ID) --- */
/* 1. Target the link to Indonesia (Any link starting with /id/) */
.VPNavBarTranslations a[href^="/id/"]::before,
.VPNavBarTranslations a[href^="/id/"] span::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 480'%3E%3Cpath fill='%23e12127' d='M0 0h640v240H0z'/%3E%3Cpath fill='%23fff' d='M0 240h640v240H0z'/%3E%3C/svg%3E");
}
/* 2. Target the active Indonesia title (when on /id/) */
html[lang="id"] .VPNavBarTranslations .items .title::before {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 480'%3E%3Cpath fill='%23e12127' d='M0 0h640v240H0z'/%3E%3Cpath fill='%23fff' d='M0 240h640v240H0z'/%3E%3C/svg%3E");
}