Full Code of antfu/vue-minesweeper for AI

main 46214fc89a7e cached
34 files
34.1 KB
10.0k tokens
21 symbols
1 requests
Download .txt
Repository: antfu/vue-minesweeper
Branch: main
Commit: 46214fc89a7e
Files: 34
Total size: 34.1 KB

Directory structure:
gitextract_a788da4l/

├── .eslintignore
├── .eslintrc
├── .gitignore
├── .npmrc
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── LICENSE
├── README.md
├── auto-imports.d.ts
├── components.d.ts
├── index.html
├── netlify.toml
├── package.json
├── public/
│   └── robots.txt
├── shims.d.ts
├── src/
│   ├── App.vue
│   ├── components/
│   │   ├── Confetti.vue
│   │   ├── Footer.vue
│   │   ├── MineBlock.vue
│   │   └── README.md
│   ├── composables/
│   │   ├── dark.ts
│   │   ├── index.ts
│   │   ├── logic.ts
│   │   └── storage.ts
│   ├── main.ts
│   ├── pages/
│   │   ├── README.md
│   │   └── index.vue
│   ├── styles/
│   │   └── main.css
│   └── types.ts
├── test/
│   ├── __snapshots__/
│   │   └── component.test.ts.snap
│   └── basic.test.ts
├── tsconfig.json
├── unocss.config.ts
└── vite.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintignore
================================================
dist
node_modules
public


================================================
FILE: .eslintrc
================================================
{
  "extends": "@antfu"
}


================================================
FILE: .gitignore
================================================
.DS_Store
.vite-ssg-dist
.vite-ssg-temp
*.local
dist
dist-ssr
node_modules
.idea/
*.log


================================================
FILE: .npmrc
================================================
shamefully-hoist=true


================================================
FILE: .vscode/extensions.json
================================================
{
  "recommendations": [
    "antfu.vite",
    "antfu.iconify",
    "antfu.unocss",
    "johnsoncodehk.volar",
    "dbaeumer.vscode-eslint"
  ]
}


================================================
FILE: .vscode/settings.json
================================================
{
  "cSpell.words": ["Vitesse"],
  "prettier.enable": false,
  "typescript.tsdk": "node_modules/typescript/lib",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true,
  },
  "files.associations": {
    "*.css": "postcss",
  },
}


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020-PRESENT Anthony Fu

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Vue Minesweeper

Code from live screencasting - A tiny minesweeper clone in Vue 3

> Deploy: https://vuesweeper.netlify.app/
>
> Live Stream: https://live.bilibili.com/835461 (it's over)
>
> Recording: https://www.bilibili.com/video/BV1ia411b7jY/

🚧 如果可以尽量不要提功能性 PR,留着之后直播完善(欢迎自己 Fork 改),谢谢


================================================
FILE: auto-imports.d.ts
================================================
// Generated by 'unplugin-auto-import'
// We suggest you to commit this file into source control
declare global {
  const $: typeof import('vue/macros')['$']
  const $$: typeof import('vue/macros')['$$']
  const $computed: typeof import('vue/macros')['$computed']
  const $customRef: typeof import('vue/macros')['$customRef']
  const $ref: typeof import('vue/macros')['$ref']
  const $shallowRef: typeof import('vue/macros')['$shallowRef']
  const $toRef: typeof import('vue/macros')['$toRef']
  const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
  const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
  const computed: typeof import('vue')['computed']
  const computedAsync: typeof import('@vueuse/core')['computedAsync']
  const computedEager: typeof import('@vueuse/core')['computedEager']
  const computedInject: typeof import('@vueuse/core')['computedInject']
  const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
  const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
  const controlledRef: typeof import('@vueuse/core')['controlledRef']
  const createApp: typeof import('vue')['createApp']
  const createEventHook: typeof import('@vueuse/core')['createEventHook']
  const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
  const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
  const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
  const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
  const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
  const customRef: typeof import('vue')['customRef']
  const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
  const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
  const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
  const defineComponent: typeof import('vue')['defineComponent']
  const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
  const effectScope: typeof import('vue')['effectScope']
  const EffectScope: typeof import('vue')['EffectScope']
  const extendRef: typeof import('@vueuse/core')['extendRef']
  const getCurrentInstance: typeof import('vue')['getCurrentInstance']
  const getCurrentScope: typeof import('vue')['getCurrentScope']
  const h: typeof import('vue')['h']
  const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
  const inject: typeof import('vue')['inject']
  const isDefined: typeof import('@vueuse/core')['isDefined']
  const isReadonly: typeof import('vue')['isReadonly']
  const isRef: typeof import('vue')['isRef']
  const logicAnd: typeof import('@vueuse/core')['logicAnd']
  const logicNot: typeof import('@vueuse/core')['logicNot']
  const logicOr: typeof import('@vueuse/core')['logicOr']
  const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
  const markRaw: typeof import('vue')['markRaw']
  const nextTick: typeof import('vue')['nextTick']
  const onActivated: typeof import('vue')['onActivated']
  const onBeforeMount: typeof import('vue')['onBeforeMount']
  const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
  const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
  const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
  const onDeactivated: typeof import('vue')['onDeactivated']
  const onErrorCaptured: typeof import('vue')['onErrorCaptured']
  const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
  const onLongPress: typeof import('@vueuse/core')['onLongPress']
  const onMounted: typeof import('vue')['onMounted']
  const onRenderTracked: typeof import('vue')['onRenderTracked']
  const onRenderTriggered: typeof import('vue')['onRenderTriggered']
  const onScopeDispose: typeof import('vue')['onScopeDispose']
  const onServerPrefetch: typeof import('vue')['onServerPrefetch']
  const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
  const onUnmounted: typeof import('vue')['onUnmounted']
  const onUpdated: typeof import('vue')['onUpdated']
  const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
  const provide: typeof import('vue')['provide']
  const reactify: typeof import('@vueuse/core')['reactify']
  const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
  const reactive: typeof import('vue')['reactive']
  const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
  const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
  const reactivePick: typeof import('@vueuse/core')['reactivePick']
  const readonly: typeof import('vue')['readonly']
  const ref: typeof import('vue')['ref']
  const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
  const refDebounced: typeof import('@vueuse/core')['refDebounced']
  const refDefault: typeof import('@vueuse/core')['refDefault']
  const refThrottled: typeof import('@vueuse/core')['refThrottled']
  const refWithControl: typeof import('@vueuse/core')['refWithControl']
  const resolveComponent: typeof import('vue')['resolveComponent']
  const shallowReactive: typeof import('vue')['shallowReactive']
  const shallowReadonly: typeof import('vue')['shallowReadonly']
  const shallowRef: typeof import('vue')['shallowRef']
  const syncRef: typeof import('@vueuse/core')['syncRef']
  const syncRefs: typeof import('@vueuse/core')['syncRefs']
  const templateRef: typeof import('@vueuse/core')['templateRef']
  const throttledRef: typeof import('@vueuse/core')['throttledRef']
  const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
  const toRaw: typeof import('vue')['toRaw']
  const toReactive: typeof import('@vueuse/core')['toReactive']
  const toRef: typeof import('vue')['toRef']
  const toRefs: typeof import('vue')['toRefs']
  const triggerRef: typeof import('vue')['triggerRef']
  const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
  const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
  const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
  const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
  const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
  const unref: typeof import('vue')['unref']
  const unrefElement: typeof import('@vueuse/core')['unrefElement']
  const until: typeof import('@vueuse/core')['until']
  const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
  const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
  const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
  const useAttrs: typeof import('vue')['useAttrs']
  const useBase64: typeof import('@vueuse/core')['useBase64']
  const useBattery: typeof import('@vueuse/core')['useBattery']
  const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
  const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
  const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
  const useCached: typeof import('@vueuse/core')['useCached']
  const useClamp: typeof import('@vueuse/core')['useClamp']
  const useClipboard: typeof import('@vueuse/core')['useClipboard']
  const useColorMode: typeof import('@vueuse/core')['useColorMode']
  const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
  const useCounter: typeof import('@vueuse/core')['useCounter']
  const useCssModule: typeof import('vue')['useCssModule']
  const useCssVar: typeof import('@vueuse/core')['useCssVar']
  const useCssVars: typeof import('vue')['useCssVars']
  const useCycleList: typeof import('@vueuse/core')['useCycleList']
  const useDark: typeof import('@vueuse/core')['useDark']
  const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
  const useDebounce: typeof import('@vueuse/core')['useDebounce']
  const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
  const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
  const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
  const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
  const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
  const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
  const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
  const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
  const useDraggable: typeof import('@vueuse/core')['useDraggable']
  const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
  const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
  const useElementHover: typeof import('@vueuse/core')['useElementHover']
  const useElementSize: typeof import('@vueuse/core')['useElementSize']
  const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
  const useEventBus: typeof import('@vueuse/core')['useEventBus']
  const useEventListener: typeof import('@vueuse/core')['useEventListener']
  const useEventSource: typeof import('@vueuse/core')['useEventSource']
  const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
  const useFavicon: typeof import('@vueuse/core')['useFavicon']
  const useFetch: typeof import('@vueuse/core')['useFetch']
  const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
  const useFocus: typeof import('@vueuse/core')['useFocus']
  const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
  const useFps: typeof import('@vueuse/core')['useFps']
  const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
  const useGamepad: typeof import('@vueuse/core')['useGamepad']
  const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
  const useIdle: typeof import('@vueuse/core')['useIdle']
  const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
  const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
  const useInterval: typeof import('@vueuse/core')['useInterval']
  const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
  const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
  const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
  const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
  const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
  const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
  const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
  const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
  const useMemoize: typeof import('@vueuse/core')['useMemoize']
  const useMemory: typeof import('@vueuse/core')['useMemory']
  const useMounted: typeof import('@vueuse/core')['useMounted']
  const useMouse: typeof import('@vueuse/core')['useMouse']
  const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
  const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
  const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
  const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
  const useNetwork: typeof import('@vueuse/core')['useNetwork']
  const useNow: typeof import('@vueuse/core')['useNow']
  const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
  const useOnline: typeof import('@vueuse/core')['useOnline']
  const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
  const useParallax: typeof import('@vueuse/core')['useParallax']
  const usePermission: typeof import('@vueuse/core')['usePermission']
  const usePointer: typeof import('@vueuse/core')['usePointer']
  const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
  const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
  const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
  const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
  const useRafFn: typeof import('@vueuse/core')['useRafFn']
  const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
  const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
  const useRoute: typeof import('vue-router')['useRoute']
  const useRouter: typeof import('vue-router')['useRouter']
  const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
  const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
  const useScroll: typeof import('@vueuse/core')['useScroll']
  const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
  const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
  const useShare: typeof import('@vueuse/core')['useShare']
  const useSlots: typeof import('vue')['useSlots']
  const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
  const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
  const useStorage: typeof import('@vueuse/core')['useStorage']
  const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
  const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
  const useSwipe: typeof import('@vueuse/core')['useSwipe']
  const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
  const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
  const useThrottle: typeof import('@vueuse/core')['useThrottle']
  const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
  const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
  const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
  const useTimeout: typeof import('@vueuse/core')['useTimeout']
  const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
  const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
  const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
  const useTitle: typeof import('@vueuse/core')['useTitle']
  const useToggle: typeof import('@vueuse/core')['useToggle']
  const useTransition: typeof import('@vueuse/core')['useTransition']
  const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
  const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
  const useVibrate: typeof import('@vueuse/core')['useVibrate']
  const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
  const useVModel: typeof import('@vueuse/core')['useVModel']
  const useVModels: typeof import('@vueuse/core')['useVModels']
  const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
  const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
  const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
  const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
  const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
  const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
  const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
  const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
  const watch: typeof import('vue')['watch']
  const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
  const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
  const watchEffect: typeof import('vue')['watchEffect']
  const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
  const watchOnce: typeof import('@vueuse/core')['watchOnce']
  const watchPausable: typeof import('@vueuse/core')['watchPausable']
  const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
  const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
  const whenever: typeof import('@vueuse/core')['whenever']
}
export {}


================================================
FILE: components.d.ts
================================================
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/vue-next/pull/3399

declare module 'vue' {
  export interface GlobalComponents {
    Confetti: typeof import('./src/components/Confetti.vue')['default']
    Footer: typeof import('./src/components/Footer.vue')['default']
    MineBlock: typeof import('./src/components/MineBlock.vue')['default']
  }
}

export { }


================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="icon" href="/favicon.svg" type="image/svg+xml">
  <title>Minesweeper</title>
  <meta name="description" content="Opinionated Vite Starter Template">
</head>
<body class="font-sans dark:text-white dark:bg-hex-121212">
  <div id="app"></div>
  <script>
    (function() {
      const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
      const setting = localStorage.getItem('color-schema') || 'auto'
      if (setting === 'dark' || (prefersDark && setting !== 'light'))
        document.documentElement.classList.toggle('dark', true)
    })()
  </script>
  <script type="module" src="/src/main.ts"></script>
</body>
</html>


================================================
FILE: netlify.toml
================================================
[build.environment]
  NPM_FLAGS = "--version"
  NODE_VERSION = "16"

[build]
  publish = "dist"
  command = "npx pnpm i --store=node_modules/.pnpm-store && npx pnpm run build"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200


================================================
FILE: package.json
================================================
{
  "private": true,
  "scripts": {
    "dev": "vite --port 3333 --open",
    "build": "vite build",
    "preview": "vite preview",
    "lint": "eslint .",
    "test": "vitest"
  },
  "dependencies": {
    "@vueuse/core": "^8.0.0",
    "canvas-confetti": "^1.5.1",
    "seedrandom": "^3.0.5",
    "vue": "^3.2.31",
    "vue-router": "^4.0.14"
  },
  "devDependencies": {
    "@antfu/eslint-config": "^0.18.8",
    "@iconify-json/carbon": "^1.1.1",
    "@iconify-json/mdi": "^1.1.1",
    "@types/canvas-confetti": "^1.4.2",
    "@types/node": "^17.0.21",
    "@types/seedrandom": "^3.0.2",
    "@unocss/reset": "^0.28.0",
    "@vitejs/plugin-vue": "^2.2.4",
    "@vue/test-utils": "^2.0.0-rc.18",
    "eslint": "^8.11.0",
    "jsdom": "^19.0.0",
    "pnpm": "^6.32.3",
    "typescript": "^4.6.2",
    "unocss": "^0.28.0",
    "unplugin-auto-import": "^0.6.4",
    "unplugin-vue-components": "^0.18.0",
    "vite": "^2.8.6",
    "vite-plugin-pages": "^0.21.4",
    "vitest": "^0.6.0"
  }
}


================================================
FILE: public/robots.txt
================================================
User-agent: *
Allow: /


================================================
FILE: shims.d.ts
================================================
declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}


================================================
FILE: src/App.vue
================================================
<template>
  <main font-sans p="y-10" text="center gray-700 dark:gray-200">
    <router-view />
    <Footer />
  </main>
</template>


================================================
FILE: src/components/Confetti.vue
================================================
<script setup lang="ts">
import confetti from 'canvas-confetti'

const props = defineProps<{
  passed: boolean
}>()

function congrats() {
  const defaults = {
    colors: [
      '#5D8C7B',
      '#F2D091',
      '#F2A679',
      '#D9695F',
      '#8C4646',
    ],
    shapes: ['square'],
    ticks: 500,
  } as confetti.Options
  confetti({
    ...defaults,
    particleCount: 80,
    spread: 100,
    origin: { y: 0 },
  })
  setTimeout(() => {
    confetti({
      ...defaults,
      particleCount: 50,
      angle: 60,
      spread: 80,
      origin: { x: 0 },
    })
  }, 250)
  setTimeout(() => {
    confetti({
      ...defaults,
      particleCount: 50,
      angle: 120,
      spread: 80,
      origin: { x: 1 },
    })
  }, 400)
}

watch(
  () => props.passed,
  (v) => {
    if (v)
      setTimeout(congrats, 300)
  },
  { flush: 'post' },
)
</script>

<template>
  <div />
</template>


================================================
FILE: src/components/Footer.vue
================================================
<script setup lang="ts">
import { isDark, toggleDark } from '~/composables'
</script>

<template>
  <nav text-xl mt-6 inline-flex gap-2>
    <button class="icon-btn !outline-none" @click="toggleDark()">
      <div v-if="isDark" i-carbon-moon />
      <div v-else i-carbon-sun />
    </button>

    <a
      class="icon-btn"
      i-carbon-logo-github
      rel="noreferrer"
      href="https://github.com/antfu/vue-minesweeper"
      target="_blank"
      title="GitHub"
    />
  </nav>
</template>


================================================
FILE: src/components/MineBlock.vue
================================================
<script setup lang="ts">
import type { BlockState } from '~/types'
import { isDev } from '~/composables'

defineProps<{ block: BlockState }>()

const emit = defineEmits<{
  (e: 'lrclick', event: MouseEvent): void
}>()

function whichButtons(event: MouseEvent) {
  if (event.buttons === 3)
    emit('lrclick', event)
}

const numberColors = [
  'text-transparent',
  'text-blue-500',
  'text-green-500',
  'text-yellow-500',
  'text-orange-500',
  'text-red-500',
  'text-purple-500',
  'text-pink-500',
  'text-teal-500',
]

function getBlockClass(block: BlockState) {
  if (block.flagged)
    return 'bg-gray-500/10'
  if (!block.revealed)
    return 'bg-gray-500/10 hover:bg-gray-500/20'

  return block.mine
    ? 'bg-red-500/50'
    : numberColors[block.adjacentMines]
}
</script>

<template>
  <button
    flex="~"
    items-center justify-center
    min-w-8 min-h-8 m="1px"
    border="0.5 gray-400/10"
    :class="getBlockClass(block)"
    @mousedown="whichButtons"
  >
    <template v-if="block.flagged">
      <div i-mdi-flag text-red />
    </template>
    <template v-else-if="block.revealed || isDev">
      <div v-if="block.mine" i-mdi-mine />
      <div v-else font-600>
        {{ block.adjacentMines }}
      </div>
    </template>
  </button>
</template>


================================================
FILE: src/components/README.md
================================================
## Components

Components in this dir will be auto-registered and on-demand, powered by [`unplugin-vue-components`](https://github.com/antfu/unplugin-vue-components).

### Icons

You can use icons from almost any icon sets by the power of [Iconify](https://iconify.design/).

It will only bundle the icons you use. Check out [`unplugin-icons`](https://github.com/antfu/unplugin-icons) for more details.


================================================
FILE: src/composables/dark.ts
================================================
export const isDark = useDark()
export const toggleDark = useToggle(isDark)


================================================
FILE: src/composables/index.ts
================================================
export * from './dark'
export * from './storage'


================================================
FILE: src/composables/logic.ts
================================================
import type { Ref } from 'vue'
// import type createSeedrandom from 'seedrandom'
import type { BlockState } from '~/types'

const directions = [
  [1, 1],
  [1, 0],
  [1, -1],
  [0, -1],
  [-1, -1],
  [-1, 0],
  [-1, 1],
  [0, 1],
]

type GameStatus = 'ready' | 'play' | 'won' | 'lost'

interface GameState {
  board: BlockState[][]
  mineGenerated: boolean
  status: GameStatus
  startMS?: number
  endMS?: number
}

export class GamePlay {
  state = ref() as Ref<GameState>
  // random: ReturnType<typeof createSeedrandom>

  constructor(
    public width: number,
    public height: number,
    public mines: number,
  ) {
    this.reset()
  }

  get board() {
    return this.state.value.board
  }

  get blocks() {
    return this.state.value.board.flat() as BlockState[]
  }

  reset(
    width = this.width,
    height = this.height,
    mines = this.mines,
  ) {
    this.width = width
    this.height = height
    this.mines = mines

    this.state.value = {
      mineGenerated: false,
      status: 'ready',
      board: Array.from({ length: this.height }, (_, y) =>
        Array.from({ length: this.width },
          (_, x): BlockState => ({
            x,
            y,
            adjacentMines: 0,
            revealed: false,
          }),
        ),
      ),
    }
  }

  randomRange(min: number, max: number) {
    return Math.random() * (max - min) + min
  }

  randomInt(min: number, max: number) {
    return Math.round(this.randomRange(min, max))
  }

  generateMines(state: BlockState[][], initial: BlockState) {
    const placeRandom = () => {
      const x = this.randomInt(0, this.width - 1)
      const y = this.randomInt(0, this.height - 1)
      const block = state[y][x]
      if (Math.abs(initial.x - block.x) <= 1 && Math.abs(initial.y - block.y) <= 1)
        return false
      if (block.mine)
        return false
      block.mine = true
      return true
    }
    Array.from({ length: this.mines }, () => null)
      .forEach(() => {
        let placed = false
        while (!placed)
          placed = placeRandom()
      })
    this.updateNumbers()
  }

  updateNumbers() {
    this.board.forEach((raw) => {
      raw.forEach((block) => {
        if (block.mine)
          return
        this.getSiblings(block)
          .forEach((b) => {
            if (b.mine)
              block.adjacentMines += 1
          })
      })
    })
  }

  expendZero(block: BlockState) {
    if (block.adjacentMines)
      return

    this.getSiblings(block)
      .forEach((s) => {
        if (!s.revealed) {
          if (!s.flagged)
            s.revealed = true
          this.expendZero(s)
        }
      })
  }

  onRightClick(block: BlockState) {
    if (this.state.value.status !== 'play')
      return

    if (block.revealed)
      return
    block.flagged = !block.flagged
  }

  onClick(block: BlockState) {
    if (this.state.value.status === 'ready') {
      this.state.value.status = 'play'
      this.state.value.startMS = +new Date()
    }
    if (this.state.value.status !== 'play' || block.flagged)
      return

    if (!this.state.value.mineGenerated) {
      this.generateMines(this.board, block)
      this.state.value.mineGenerated = true
    }

    block.revealed = true
    if (block.mine) {
      this.onGameOver('lost')
      return
    }

    this.expendZero(block)
  }

  getSiblings(block: BlockState) {
    return directions.map(([dx, dy]) => {
      const x2 = block.x + dx
      const y2 = block.y + dy
      if (x2 < 0 || x2 >= this.width || y2 < 0 || y2 >= this.height)
        return undefined
      return this.board[y2][x2]
    })
      .filter(Boolean) as BlockState[]
  }

  showAllMines() {
    this.board.flat().forEach((i) => {
      if (i.mine)
        i.revealed = true
    })
  }

  checkGameState() {
    if (!this.state.value.mineGenerated || this.state.value.status !== 'play')
      return
    const blocks = this.board.flat()

    if (!blocks.some(block => !block.mine && !block.revealed))
      this.onGameOver('won')
  }

  autoExpand(block: BlockState) {
    if (this.state.value.status !== 'play' || block.flagged)
      return

    const siblings = this.getSiblings(block)
    const flags = siblings.reduce((a, b) => a + (b.flagged ? 1 : 0), 0)
    const notRevealed = siblings.reduce((a, b) => a + (!b.revealed && !b.flagged ? 1 : 0), 0)
    if (flags === block.adjacentMines) {
      siblings.forEach((i) => {
        if (i.revealed || i.flagged)
          return
        i.revealed = true
        this.expendZero(i)
        if (i.mine)
          this.onGameOver('lost')
      })
    }
    const missingFlags = block.adjacentMines - flags
    if (notRevealed === missingFlags) {
      siblings.forEach((i) => {
        if (!i.revealed && !i.flagged)
          i.flagged = true
      })
    }
  }

  onGameOver(status: GameStatus) {
    this.state.value.status = status
    this.state.value.endMS = +Date.now()
    if (status === 'lost') {
      this.showAllMines()
      setTimeout(() => {
        alert('lost')
      }, 10)
    }
  }
}


================================================
FILE: src/composables/storage.ts
================================================
export const isDev = ref(false)
export const toggleDev = useToggle(isDev)


================================================
FILE: src/main.ts
================================================
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import routes from 'virtual:generated-pages'
import App from './App.vue'

import '@unocss/reset/tailwind.css'
import './styles/main.css'
import 'uno.css'

const app = createApp(App)
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes,
})
app.use(router)
app.mount('#app')


================================================
FILE: src/pages/README.md
================================================
## File-based Routing

Routes will be auto-generated for Vue files in this dir with the same file structure.
Check out [`vite-plugin-pages`](https://github.com/hannoeru/vite-plugin-pages) for more details.

### Path Aliasing

`~/` is aliased to `./src/` folder.

For example, instead of having

```ts
import { isDark } from '../../../../composables'
```

now, you can use

```ts
import { isDark } from '~/composables'
```


================================================
FILE: src/pages/index.vue
================================================
<script setup lang="ts">
// import { isDev, toggleDev } from '~/composables'
import { GamePlay } from '~/composables/logic'

const play = new GamePlay(9, 9, 10)

const now = $(useNow())
const timerMS = $computed(() => Math.round(((play.state.value.endMS ?? +now) - (play.state.value.startMS ?? +now)) / 1000))

useStorage('vuesweeper-state', play.state)
const state = $computed(() => play.board)

const mineRest = $computed(() => {
  if (!play.state.value.mineGenerated)
    return play.mines
  return play.blocks.reduce((a, b) => a - (b.flagged ? 1 : 0), play.mines)
})

function newGame(difficulty: 'easy' | 'medium' | 'hard') {
  switch (difficulty) {
    case 'easy':
      play.reset(9, 9, 10)
      break
    case 'medium':
      play.reset(16, 16, 40)
      break
    case 'hard':
      play.reset(16, 30, 99)
      break
  }
}

watchEffect(() => {
  play.checkGameState()
})
</script>

<template>
  <div>
    Minesweeper

    <div flex="~ gap1" justify-center p4>
      <button btn @click="play.reset()">
        New Game
      </button>
      <button btn @click="newGame('easy')">
        Easy
      </button>
      <button btn @click="newGame('medium')">
        Medium
      </button>
      <button btn @click="newGame('hard')">
        Hard
      </button>
    </div>

    <div flex="~ gap-10" justify-center>
      <div font-mono text-2xl flex="~ gap-1" items-center>
        <div i-carbon-timer />
        {{ timerMS }}
      </div>
      <div font-mono text-2xl flex="~ gap-1" items-center>
        <div i-mdi-mine />
        {{ mineRest }}
      </div>
    </div>

    <div p5 w-full overflow-auto>
      <div
        v-for="row, y in state"
        :key="y"
        flex="~"
        items-center justify-center w-max ma
      >
        <MineBlock
          v-for="block, x in row" :key="x"
          :block="block"
          @click="play.onClick(block)"
          @lrclick="play.autoExpand(block)"
          @contextmenu.prevent="play.onRightClick(block)"
        />
      </div>
    </div>

    <!-- <div flex="~ gap-1" justify-center>
      <button btn @click="toggleDev()">
        {{ isDev ? 'DEV' : 'NORMAL' }}
      </button>
    </div> -->

    <Confetti :passed="play.state.value.status === 'won'" />
  </div>
</template>


================================================
FILE: src/styles/main.css
================================================
html,
body,
#app {
  height: 100%;
  margin: 0;
  padding: 0;
}

html.dark {
  background: #121212;
}


================================================
FILE: src/types.ts
================================================
export interface BlockState {
  x: number
  y: number
  revealed: boolean
  mine?: boolean
  flagged?: boolean
  adjacentMines: number
}


================================================
FILE: test/__snapshots__/component.test.ts.snap
================================================
// Vitest Snapshot v1

exports[`Counter.vue > should render 1`] = `"<div>10 <button class=\\"inc\\"> + </button><button class=\\"dec\\"> - </button></div>"`;


================================================
FILE: test/basic.test.ts
================================================
import { describe, expect, it } from 'vitest'

describe('Hi', () => {
  it('should works', () => {
    expect(1 + 1).toEqual(2)
  })
})


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "baseUrl": ".",
    "module": "ESNext",
    "target": "es2016",
    "lib": ["DOM", "ESNext"],
    "strict": true,
    "esModuleInterop": true,
    "incremental": false,
    "skipLibCheck": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "noUnusedLocals": true,
    "strictNullChecks": true,
    "forceConsistentCasingInFileNames": true,
    "types": [
      "vite/client",
      "vite-plugin-pages/client"
    ],
    "paths": {
      "~/*": ["src/*"]
    }
  },
  "exclude": ["dist", "node_modules"]
}


================================================
FILE: unocss.config.ts
================================================
import {
  defineConfig,
  presetAttributify,
  presetIcons,
  presetUno,
  transformerDirectives,
  transformerVariantGroup,
} from 'unocss'

export default defineConfig({
  shortcuts: [
    ['btn', 'px-4 py-1 rounded inline-block bg-teal-600 text-white cursor-pointer hover:bg-teal-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'],
    ['icon-btn', 'text-[0.9em] inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-teal-600'],
  ],
  presets: [
    presetUno(),
    presetAttributify(),
    presetIcons({
      scale: 1.2,
      warn: true,
    }),
  ],
  transformers: [
    transformerDirectives(),
    transformerVariantGroup(),
  ],
})


================================================
FILE: vite.config.ts
================================================
/// <reference types="vitest" />

import path from 'path'
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import Pages from 'vite-plugin-pages'
import Components from 'unplugin-vue-components/vite'
import AutoImport from 'unplugin-auto-import/vite'
import Unocss from 'unocss/vite'

export default defineConfig({
  resolve: {
    alias: {
      '~/': `${path.resolve(__dirname, 'src')}/`,
    },
  },
  plugins: [
    Vue({
      reactivityTransform: true,
    }),

    // https://github.com/hannoeru/vite-plugin-pages
    Pages(),

    // https://github.com/antfu/unplugin-auto-import
    AutoImport({
      imports: [
        'vue',
        'vue/macros',
        'vue-router',
        '@vueuse/core',
      ],
      dts: true,
    }),

    // https://github.com/antfu/vite-plugin-components
    Components({
      dts: true,
    }),

    // https://github.com/antfu/unocss
    // see unocss.config.ts for config
    Unocss(),
  ],

  // https://github.com/vitest-dev/vitest
  test: {
    environment: 'jsdom',
  },
})
Download .txt
gitextract_a788da4l/

├── .eslintignore
├── .eslintrc
├── .gitignore
├── .npmrc
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── LICENSE
├── README.md
├── auto-imports.d.ts
├── components.d.ts
├── index.html
├── netlify.toml
├── package.json
├── public/
│   └── robots.txt
├── shims.d.ts
├── src/
│   ├── App.vue
│   ├── components/
│   │   ├── Confetti.vue
│   │   ├── Footer.vue
│   │   ├── MineBlock.vue
│   │   └── README.md
│   ├── composables/
│   │   ├── dark.ts
│   │   ├── index.ts
│   │   ├── logic.ts
│   │   └── storage.ts
│   ├── main.ts
│   ├── pages/
│   │   ├── README.md
│   │   └── index.vue
│   ├── styles/
│   │   └── main.css
│   └── types.ts
├── test/
│   ├── __snapshots__/
│   │   └── component.test.ts.snap
│   └── basic.test.ts
├── tsconfig.json
├── unocss.config.ts
└── vite.config.ts
Download .txt
SYMBOL INDEX (21 symbols across 3 files)

FILE: components.d.ts
  type GlobalComponents (line 6) | interface GlobalComponents {

FILE: src/composables/logic.ts
  type GameStatus (line 16) | type GameStatus = 'ready' | 'play' | 'won' | 'lost'
  type GameState (line 18) | interface GameState {
  class GamePlay (line 26) | class GamePlay {
    method constructor (line 30) | constructor(
    method board (line 38) | get board() {
    method blocks (line 42) | get blocks() {
    method reset (line 46) | reset(
    method randomRange (line 71) | randomRange(min: number, max: number) {
    method randomInt (line 75) | randomInt(min: number, max: number) {
    method generateMines (line 79) | generateMines(state: BlockState[][], initial: BlockState) {
    method updateNumbers (line 100) | updateNumbers() {
    method expendZero (line 114) | expendZero(block: BlockState) {
    method onRightClick (line 128) | onRightClick(block: BlockState) {
    method onClick (line 137) | onClick(block: BlockState) {
    method getSiblings (line 159) | getSiblings(block: BlockState) {
    method showAllMines (line 170) | showAllMines() {
    method checkGameState (line 177) | checkGameState() {
    method autoExpand (line 186) | autoExpand(block: BlockState) {
    method onGameOver (line 212) | onGameOver(status: GameStatus) {

FILE: src/types.ts
  type BlockState (line 1) | interface BlockState {
Condensed preview — 34 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (38K chars).
[
  {
    "path": ".eslintignore",
    "chars": 25,
    "preview": "dist\nnode_modules\npublic\n"
  },
  {
    "path": ".eslintrc",
    "chars": 26,
    "preview": "{\n  \"extends\": \"@antfu\"\n}\n"
  },
  {
    "path": ".gitignore",
    "chars": 88,
    "preview": ".DS_Store\n.vite-ssg-dist\n.vite-ssg-temp\n*.local\ndist\ndist-ssr\nnode_modules\n.idea/\n*.log\n"
  },
  {
    "path": ".npmrc",
    "chars": 22,
    "preview": "shamefully-hoist=true\n"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 146,
    "preview": "{\n  \"recommendations\": [\n    \"antfu.vite\",\n    \"antfu.iconify\",\n    \"antfu.unocss\",\n    \"johnsoncodehk.volar\",\n    \"dbae"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 241,
    "preview": "{\n  \"cSpell.words\": [\"Vitesse\"],\n  \"prettier.enable\": false,\n  \"typescript.tsdk\": \"node_modules/typescript/lib\",\n  \"edit"
  },
  {
    "path": "LICENSE",
    "chars": 1075,
    "preview": "MIT License\n\nCopyright (c) 2020-PRESENT Anthony Fu\n\nPermission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "README.md",
    "chars": 293,
    "preview": "# Vue Minesweeper\n\nCode from live screencasting - A tiny minesweeper clone in Vue 3\n\n> Deploy: https://vuesweeper.netlif"
  },
  {
    "path": "auto-imports.d.ts",
    "chars": 15941,
    "preview": "// Generated by 'unplugin-auto-import'\n// We suggest you to commit this file into source control\ndeclare global {\n  cons"
  },
  {
    "path": "components.d.ts",
    "chars": 450,
    "preview": "// generated by unplugin-vue-components\n// We suggest you to commit this file into source control\n// Read more: https://"
  },
  {
    "path": "index.html",
    "chars": 818,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, in"
  },
  {
    "path": "netlify.toml",
    "chars": 241,
    "preview": "[build.environment]\n  NPM_FLAGS = \"--version\"\n  NODE_VERSION = \"16\"\n\n[build]\n  publish = \"dist\"\n  command = \"npx pnpm i "
  },
  {
    "path": "package.json",
    "chars": 988,
    "preview": "{\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"vite --port 3333 --open\",\n    \"build\": \"vite build\",\n    \"preview\": \"vit"
  },
  {
    "path": "public/robots.txt",
    "chars": 23,
    "preview": "User-agent: *\nAllow: /\n"
  },
  {
    "path": "shims.d.ts",
    "chars": 147,
    "preview": "declare module '*.vue' {\n  import type { DefineComponent } from 'vue'\n  const component: DefineComponent<{}, {}, any>\n  "
  },
  {
    "path": "src/App.vue",
    "chars": 133,
    "preview": "<template>\n  <main font-sans p=\"y-10\" text=\"center gray-700 dark:gray-200\">\n    <router-view />\n    <Footer />\n  </main>"
  },
  {
    "path": "src/components/Confetti.vue",
    "chars": 898,
    "preview": "<script setup lang=\"ts\">\nimport confetti from 'canvas-confetti'\n\nconst props = defineProps<{\n  passed: boolean\n}>()\n\nfun"
  },
  {
    "path": "src/components/Footer.vue",
    "chars": 499,
    "preview": "<script setup lang=\"ts\">\nimport { isDark, toggleDark } from '~/composables'\n</script>\n\n<template>\n  <nav text-xl mt-6 in"
  },
  {
    "path": "src/components/MineBlock.vue",
    "chars": 1272,
    "preview": "<script setup lang=\"ts\">\nimport type { BlockState } from '~/types'\nimport { isDev } from '~/composables'\n\ndefineProps<{ "
  },
  {
    "path": "src/components/README.md",
    "chars": 403,
    "preview": "## Components\n\nComponents in this dir will be auto-registered and on-demand, powered by [`unplugin-vue-components`](http"
  },
  {
    "path": "src/composables/dark.ts",
    "chars": 76,
    "preview": "export const isDark = useDark()\nexport const toggleDark = useToggle(isDark)\n"
  },
  {
    "path": "src/composables/index.ts",
    "chars": 49,
    "preview": "export * from './dark'\nexport * from './storage'\n"
  },
  {
    "path": "src/composables/logic.ts",
    "chars": 5023,
    "preview": "import type { Ref } from 'vue'\n// import type createSeedrandom from 'seedrandom'\nimport type { BlockState } from '~/type"
  },
  {
    "path": "src/composables/storage.ts",
    "chars": 74,
    "preview": "export const isDev = ref(false)\nexport const toggleDev = useToggle(isDev)\n"
  },
  {
    "path": "src/main.ts",
    "chars": 406,
    "preview": "import { createApp } from 'vue'\nimport { createRouter, createWebHistory } from 'vue-router'\nimport routes from 'virtual:"
  },
  {
    "path": "src/pages/README.md",
    "chars": 422,
    "preview": "## File-based Routing\n\nRoutes will be auto-generated for Vue files in this dir with the same file structure.\nCheck out ["
  },
  {
    "path": "src/pages/index.vue",
    "chars": 2247,
    "preview": "<script setup lang=\"ts\">\n// import { isDev, toggleDev } from '~/composables'\nimport { GamePlay } from '~/composables/log"
  },
  {
    "path": "src/styles/main.css",
    "chars": 102,
    "preview": "html,\nbody,\n#app {\n  height: 100%;\n  margin: 0;\n  padding: 0;\n}\n\nhtml.dark {\n  background: #121212;\n}\n"
  },
  {
    "path": "src/types.ts",
    "chars": 137,
    "preview": "export interface BlockState {\n  x: number\n  y: number\n  revealed: boolean\n  mine?: boolean\n  flagged?: boolean\n  adjacen"
  },
  {
    "path": "test/__snapshots__/component.test.ts.snap",
    "chars": 158,
    "preview": "// Vitest Snapshot v1\n\nexports[`Counter.vue > should render 1`] = `\"<div>10 <button class=\\\\\"inc\\\\\"> + </button><button "
  },
  {
    "path": "test/basic.test.ts",
    "chars": 136,
    "preview": "import { describe, expect, it } from 'vitest'\n\ndescribe('Hi', () => {\n  it('should works', () => {\n    expect(1 + 1).toE"
  },
  {
    "path": "tsconfig.json",
    "chars": 556,
    "preview": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"module\": \"ESNext\",\n    \"target\": \"es2016\",\n    \"lib\": [\"DOM\", \"ESNext\""
  },
  {
    "path": "unocss.config.ts",
    "chars": 728,
    "preview": "import {\n  defineConfig,\n  presetAttributify,\n  presetIcons,\n  presetUno,\n  transformerDirectives,\n  transformerVariantG"
  },
  {
    "path": "vite.config.ts",
    "chars": 1045,
    "preview": "/// <reference types=\"vitest\" />\n\nimport path from 'path'\nimport { defineConfig } from 'vite'\nimport Vue from '@vitejs/p"
  }
]

About this extraction

This page contains the full source code of the antfu/vue-minesweeper GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 34 files (34.1 KB), approximately 10.0k tokens, and a symbol index with 21 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!