Skip to content

Commit fa4835e

Browse files
committed
docs: add open in Vuetify Bin functionality
1 parent 5f8208f commit fa4835e

File tree

5 files changed

+90
-2
lines changed

5 files changed

+90
-2
lines changed

packages/docs/auto-imports.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ declare global {
1919
const camelCase: typeof import('lodash-es')['camelCase']
2020
const camelize: typeof import('vue')['camelize']
2121
const cleanCache: typeof import('./src/utils/pwa')['cleanCache']
22+
const compressAndEncode: typeof import('./src/composables/bin')['compressAndEncode']
2223
const computed: typeof import('vue')['computed']
2324
const configureMarkdown: typeof import('./src/utils/markdown-it')['configureMarkdown']
2425
const copyElementContent: typeof import('./src/utils/helpers')['copyElementContent']
@@ -28,6 +29,7 @@ declare global {
2829
const createOne: typeof import('@vuetify/one')['createOne']
2930
const createPinia: typeof import('pinia')['createPinia']
3031
const customRef: typeof import('vue')['customRef']
32+
const decodeAndDecompress: typeof import('./src/composables/bin')['decodeAndDecompress']
3133
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
3234
const defineComponent: typeof import('vue')['defineComponent']
3335
const defineStore: typeof import('pinia')['defineStore']
@@ -117,6 +119,7 @@ declare global {
117119
const useAppStore: typeof import('./src/stores/app')['useAppStore']
118120
const useAttrs: typeof import('vue')['useAttrs']
119121
const useAuthStore: typeof import('@vuetify/one')['useAuthStore']
122+
const useBin: typeof import('./src/composables/bin')['useBin']
120123
const useCommitsStore: typeof import('./src/stores/commits')['useCommitsStore']
121124
const useCosmic: typeof import('./src/composables/cosmic')['useCosmic']
122125
const useCssModule: typeof import('vue')['useCssModule']
@@ -206,6 +209,7 @@ declare module 'vue' {
206209
readonly camelCase: UnwrapRef<typeof import('lodash-es')['camelCase']>
207210
readonly camelize: UnwrapRef<typeof import('vue')['camelize']>
208211
readonly cleanCache: UnwrapRef<typeof import('./src/utils/pwa')['cleanCache']>
212+
readonly compressAndEncode: UnwrapRef<typeof import('./src/composables/bin')['compressAndEncode']>
209213
readonly computed: UnwrapRef<typeof import('vue')['computed']>
210214
readonly configureMarkdown: UnwrapRef<typeof import('./src/utils/markdown-it')['configureMarkdown']>
211215
readonly copyElementContent: UnwrapRef<typeof import('./src/utils/helpers')['copyElementContent']>
@@ -214,6 +218,7 @@ declare module 'vue' {
214218
readonly createOne: UnwrapRef<typeof import('@vuetify/one')['createOne']>
215219
readonly createPinia: UnwrapRef<typeof import('pinia')['createPinia']>
216220
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
221+
readonly decodeAndDecompress: UnwrapRef<typeof import('./src/composables/bin')['decodeAndDecompress']>
217222
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
218223
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
219224
readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
@@ -298,6 +303,7 @@ declare module 'vue' {
298303
readonly useAppStore: UnwrapRef<typeof import('./src/stores/app')['useAppStore']>
299304
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
300305
readonly useAuthStore: UnwrapRef<typeof import('@vuetify/one')['useAuthStore']>
306+
readonly useBin: UnwrapRef<typeof import('./src/composables/bin')['useBin']>
301307
readonly useCommitsStore: UnwrapRef<typeof import('./src/stores/commits')['useCommitsStore']>
302308
readonly useCosmic: UnwrapRef<typeof import('./src/composables/cosmic')['useCosmic']>
303309
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>

packages/docs/src/components/app/Markup.vue

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@
3434
v-if="isHovering"
3535
:key="icon"
3636
:icon="icon"
37-
class="text-disabled me-3 mt-1 app-markup-btn position-absolute right-0 top-0"
37+
class="text-disabled me-3 mt-2 app-markup-btn position-absolute right-0 top-0"
3838
density="comfortable"
39+
size="small"
3940
v-bind="activatorProps"
4041
variant="text"
4142
@click="copy"
@@ -46,6 +47,26 @@
4647
<span>{{ t('copy-source') }}</span>
4748
</v-tooltip>
4849

50+
<v-tooltip location="start">
51+
<template #activator="{ props: activatorProps }">
52+
<v-fade-transition>
53+
<v-btn
54+
v-if="isHovering"
55+
:key="icon"
56+
class="text-disabled me-12 mt-2 app-markup-btn position-absolute right-0 top-0"
57+
density="comfortable"
58+
icon="$vuetify-bin"
59+
size="small"
60+
v-bind="activatorProps"
61+
variant="text"
62+
@click="bin"
63+
/>
64+
</v-fade-transition>
65+
</template>
66+
67+
<span>{{ t('open-in-vuetify-bin') }}</span>
68+
</v-tooltip>
69+
4970
<div class="pa-4 pe-12">
5071
<slot>
5172
<pre v-if="inline" :class="className">
@@ -116,6 +137,17 @@
116137
const className = computed(() => `language-${props.language}`)
117138
const icon = computed(() => clicked.value ? 'mdi-check' : 'mdi-clipboard-text-outline')
118139
140+
async function bin () {
141+
const el = root.value?.$el.querySelector('code')
142+
const code = props.code || el?.innerText || ''
143+
const language = props.language || 'markdown'
144+
const title = props.resource
145+
146+
const compressed = useBin(code, language, title)
147+
148+
window.open(compressed, '_blank')
149+
}
150+
119151
async function copy () {
120152
const el = root.value?.$el.querySelector('code')
121153
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Utilities
2+
import { unzlibSync, zlibSync } from 'fflate'
3+
4+
export function compressAndEncode (str: string) {
5+
// Convert the string to a Uint8Array
6+
const u8 = new TextEncoder().encode(str)
7+
8+
// Compress the Uint8Array using zlib
9+
const compressed = zlibSync(u8)
10+
11+
// Convert the compressed data to a binary string
12+
const binary = String.fromCodePoint(...compressed)
13+
14+
// Base64 encode the binary string
15+
const encoded = btoa(binary)
16+
17+
// Make it URL-safe by replacing '+' with '-', '/' with '_', and removing '='
18+
return encoded.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
19+
}
20+
21+
export function decodeAndDecompress (encoded: string) {
22+
// Make the encoded string URL-safe again by reversing previous replacements
23+
const urlSafeEncoded = encoded.replace(/-/g, '+').replace(/_/g, '/')
24+
25+
// Decode the Base64 encoded string to binary
26+
const binary = atob(urlSafeEncoded)
27+
28+
// Convert the binary string to a Uint8Array
29+
const compressed = new Uint8Array([...binary].map(c => c.codePointAt(0)!))
30+
31+
// Decompress the Uint8Array using zlib
32+
const decompressed = unzlibSync(compressed)
33+
34+
// Convert the decompressed data back to a string
35+
return new TextDecoder().decode(decompressed)
36+
}
37+
38+
export function useBin (code: string, language: string, _title?: string) {
39+
const hash = compressAndEncode(code)
40+
const map: { [key: string]: string } = {
41+
bash: 'markdown',
42+
js: 'javascript',
43+
ts: 'typescript',
44+
}
45+
46+
const title = _title ? `&title=${_title}` : ''
47+
return `https://bin.vuetifyjs.com?code=${hash}&lang=${map[language] ?? language}${title}`
48+
}

packages/docs/src/i18n/messages/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@
241241
"notifications": "Notifications",
242242
"not-found": "Not Found",
243243
"open-github-release": "Open GitHub release",
244+
"open-in-vuetify-bin": "Open in Vuetify Bin",
244245
"open-issues": "Open issues",
245246
"options": "Options",
246247
"overlays": "Overlays",

packages/docs/src/utils/markdown-it-rules.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ function addCodeRules (md: MarkdownIt) {
99
md.renderer.rules.fence = function (tokens, idx, options, env, self) {
1010
const handler = fence || self.renderToken
1111
const token = tokens[idx]
12+
const lang = extractLang(token.info || '')
1213

13-
return `<AppMarkup resource="${token?.attrs?.[0][1] ?? ''}" class="mb-4">${handler(tokens, idx, options, env, self)}</AppMarkup>`
14+
return `<AppMarkup resource="${token?.attrs?.[0][1] ?? ''}" language="${lang}" class="mb-4">${handler(tokens, idx, options, env, self)}</AppMarkup>`
1415
}
1516
md.renderer.rules.code_inline = function (tokens, idx) {
1617
const token = tokens[idx]

0 commit comments

Comments
 (0)