@vahy_the/vue-dev-debug 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # vue-dev-debug 🔍
2
+
3
+ Beautiful JSON debug viewer component for Vue 3. Automatically hidden in production builds.
4
+
5
+ ![Demo](https://via.placeholder.com/800x400/1a1a2e/f472b6?text=Vue+Dev+Debug)
6
+
7
+ ## Features
8
+
9
+ - 🎨 Beautiful cyberpunk-inspired dark theme
10
+ - 🌳 Collapsible JSON tree view
11
+ - 🔍 Expand/Collapse all nodes
12
+ - 📋 Copy JSON to clipboard
13
+ - ↗️ Open in new window
14
+ - 🪟 Floating/draggable mode
15
+ - ⛶ Fullscreen mode
16
+ - 🚀 Auto-hidden in production (no runtime overhead)
17
+ - 📦 Zero dependencies (only Vue 3)
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install vue-dev-debug
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Option 1: Global Plugin (Recommended)
28
+
29
+ ```ts
30
+ // main.ts
31
+ import { createApp } from 'vue'
32
+ import VueDevDebug from 'vue-dev-debug'
33
+ import 'vue-dev-debug/style.css'
34
+
35
+ const app = createApp(App)
36
+ app.use(VueDevDebug)
37
+ app.mount('#app')
38
+ ```
39
+
40
+ Then use it anywhere in your templates:
41
+
42
+ ```vue
43
+ <template>
44
+ <DevDebug :data="myObject" />
45
+ </template>
46
+ ```
47
+
48
+ ### Option 2: Local Import
49
+
50
+ ```vue
51
+ <script setup lang="ts">
52
+ import { DevDebug } from 'vue-dev-debug'
53
+ import 'vue-dev-debug/style.css'
54
+
55
+ const user = {
56
+ name: 'John Doe',
57
+ email: 'john@example.com',
58
+ roles: ['admin', 'user'],
59
+ }
60
+ </script>
61
+
62
+ <template>
63
+ <DevDebug :data="user" title="User Data" />
64
+ </template>
65
+ ```
66
+
67
+ ## Props
68
+
69
+ | Prop | Type | Default | Description |
70
+ |------|------|---------|-------------|
71
+ | `data` | `unknown` | **required** | Data to display in the debug viewer |
72
+ | `title` | `string` | `'Debug'` | Title for the debug panel |
73
+ | `maxHeight` | `string` | `'300px'` | Maximum height of content area |
74
+ | `collapsed` | `boolean` | `false` | Start in collapsed state |
75
+ | `draggable` | `boolean` | `false` | Enable floating/draggable mode |
76
+ | `forceShow` | `boolean` | `false` | Force show in production (use with caution!) |
77
+
78
+ ## Auto Title Extraction
79
+
80
+ If you pass an object with a single key and no title, the key name becomes the title:
81
+
82
+ ```vue
83
+ <!-- Title will be "userData" -->
84
+ <DevDebug :data="{ userData: user }" />
85
+
86
+ <!-- Same as -->
87
+ <DevDebug :data="user" title="userData" />
88
+ ```
89
+
90
+ ## Plugin Options
91
+
92
+ ```ts
93
+ app.use(VueDevDebug, {
94
+ // Force show in production (not recommended)
95
+ forceShow: false,
96
+
97
+ // Custom component name
98
+ componentName: 'Debug'
99
+ })
100
+ ```
101
+
102
+ ## Styling
103
+
104
+ The component uses scoped CSS with a cyberpunk-inspired theme:
105
+
106
+ - 💜 Purple keys
107
+ - 💚 Green strings
108
+ - 💛 Yellow numbers
109
+ - 💗 Pink booleans
110
+ - ❌ Red nulls
111
+ - 🩵 Cyan values
112
+
113
+ All styles are isolated and won't affect your app.
114
+
115
+ ## Development Only
116
+
117
+ By default, `DevDebug` renders nothing in production builds. This is detected via `import.meta.env.DEV` (Vite).
118
+
119
+ To force rendering in production (for debugging purposes), use the `forceShow` prop:
120
+
121
+ ```vue
122
+ <DevDebug :data="data" force-show />
123
+ ```
124
+
125
+ Or globally via plugin options:
126
+
127
+ ```ts
128
+ app.use(VueDevDebug, { forceShow: true })
129
+ ```
130
+
131
+ ## Keyboard Shortcuts (when in fullscreen)
132
+
133
+ - `Escape` - Exit fullscreen
134
+
135
+ ## Browser Support
136
+
137
+ - Chrome / Edge (latest)
138
+ - Firefox (latest)
139
+ - Safari (latest)
140
+
141
+ ## License
142
+
143
+ MIT
144
+
package/dist/index.cjs ADDED
@@ -0,0 +1,77 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),W={key:0,class:"json-line"},Y={class:"json-key"},q={key:1,class:"json-comma"},X={key:1,class:"json-line json-expandable"},U={class:"json-key"},G={class:"json-bracket"},K={class:"json-bracket"},Q={key:1,class:"json-comma"},R={key:2,class:"json-node"},Z={class:"json-line json-expandable"},ee={class:"json-key"},te={class:"json-bracket"},ne={class:"json-children"},oe={class:"json-line"},ae={class:"json-bracket"},le={key:0,class:"json-comma"},se=e.defineComponent({__name:"DevDebugJsonNode",props:{data:{},keyName:{default:""},root:{type:Boolean,default:!1},isLast:{type:Boolean,default:!0}},setup(o){const a=o,s=e.ref(!0),y=e.inject("expandAll",{value:0}),j=e.inject("collapseAll",{value:0});e.watch(()=>y.value,()=>{s.value=!0}),e.watch(()=>j.value,()=>{s.value=!1});const t=e.computed(()=>a.data===null?"null":Array.isArray(a.data)?"array":typeof a.data),x=e.computed(()=>t.value==="object"||t.value==="array"),E=e.computed(()=>t.value==="array"?a.data.length:t.value==="object"?Object.keys(a.data).length:0),p=e.computed(()=>t.value==="array"?a.data.map((i,l)=>({key:String(l),value:i})):t.value==="object"?Object.entries(a.data).map(([i,l])=>({key:i,value:l})):[]),r=e.computed(()=>t.value==="array"?"[":"{"),m=e.computed(()=>t.value==="array"?"]":"}"),c=e.computed(()=>t.value==="array"?`[ ${E.value} items ]`:`{ ${E.value} keys }`),v=e.computed(()=>t.value==="string"?`"${a.data}"`:t.value==="null"?"null":String(a.data)),h=e.computed(()=>{const i="json-value";switch(t.value){case"string":return`${i} json-string`;case"number":return`${i} json-number`;case"boolean":return`${i} json-boolean`;case"null":return`${i} json-null`;default:return i}}),k=()=>{x.value&&(s.value=!s.value)};return(i,l)=>{const b=e.resolveComponent("DevDebugJsonNode",!0);return x.value?s.value?(e.openBlock(),e.createElementBlock("div",R,[e.createElementVNode("div",Z,[e.createElementVNode("span",{class:"json-toggle json-toggle--expanded",onClick:k},"▼"),o.keyName?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createElementVNode("span",ee,'"'+e.toDisplayString(o.keyName)+'"',1),l[2]||(l[2]=e.createElementVNode("span",{class:"json-colon"},": ",-1))],64)):e.createCommentVNode("",!0),e.createElementVNode("span",te,e.toDisplayString(r.value),1)]),e.createElementVNode("div",ne,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(p.value,(_,N)=>(e.openBlock(),e.createBlock(b,{key:_.key,data:_.value,"key-name":t.value==="array"?"":_.key,"is-last":N===p.value.length-1},null,8,["data","key-name","is-last"]))),128))]),e.createElementVNode("div",oe,[e.createElementVNode("span",ae,e.toDisplayString(m.value),1),o.isLast?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("span",le,","))])])):(e.openBlock(),e.createElementBlock("div",X,[e.createElementVNode("span",{class:"json-toggle",onClick:k},"▶"),o.keyName?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createElementVNode("span",U,'"'+e.toDisplayString(o.keyName)+'"',1),l[1]||(l[1]=e.createElementVNode("span",{class:"json-colon"},": ",-1))],64)):e.createCommentVNode("",!0),e.createElementVNode("span",G,e.toDisplayString(r.value),1),e.createElementVNode("span",{class:"json-preview",onClick:k},e.toDisplayString(c.value),1),e.createElementVNode("span",K,e.toDisplayString(m.value),1),o.isLast?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("span",Q,","))])):(e.openBlock(),e.createElementBlock("div",W,[o.keyName?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createElementVNode("span",Y,'"'+e.toDisplayString(o.keyName)+'"',1),l[0]||(l[0]=e.createElementVNode("span",{class:"json-colon"},": ",-1))],64)):e.createCommentVNode("",!0),e.createElementVNode("span",{class:e.normalizeClass(h.value)},e.toDisplayString(v.value),3),o.isLast?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("span",q,","))]))}}}),S=(o,a)=>{const s=o.__vccOpts||o;for(const[y,j]of a)s[y]=j;return s},B=S(se,[["__scopeId","data-v-2d2f26de"]]),re={class:"dev-debug__title"},ce={class:"dev-debug__actions"},ie=["title"],de={class:"json-tree"},ue=`
2
+ * { margin: 0; padding: 0; box-sizing: border-box; }
3
+ body {
4
+ font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
5
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
6
+ color: #a5f3fc;
7
+ padding: 20px;
8
+ min-height: 100vh;
9
+ }
10
+ h1 {
11
+ color: #f472b6;
12
+ margin-bottom: 16px;
13
+ font-size: 16px;
14
+ text-transform: uppercase;
15
+ letter-spacing: 0.5px;
16
+ }
17
+ .json-tree {
18
+ line-height: 1.6;
19
+ font-size: 13px;
20
+ }
21
+ .json-line { white-space: nowrap; }
22
+ .json-toggle {
23
+ display: inline-block;
24
+ width: 16px;
25
+ color: #64748b;
26
+ cursor: pointer;
27
+ user-select: none;
28
+ font-size: 10px;
29
+ }
30
+ .json-toggle:hover { color: #f472b6; }
31
+ .json-toggle--expanded { color: #a5f3fc; }
32
+ .json-children {
33
+ padding-left: 20px;
34
+ border-left: 1px solid #3b3b5c;
35
+ margin-left: 7px;
36
+ }
37
+ .json-key { color: #c084fc; font-weight: 500; }
38
+ .json-colon { color: #64748b; margin-right: 4px; }
39
+ .json-comma { color: #64748b; }
40
+ .json-bracket { color: #64748b; }
41
+ .json-value { color: #a5f3fc; }
42
+ .json-string { color: #6ee7b7; }
43
+ .json-number { color: #fbbf24; }
44
+ .json-boolean { color: #f472b6; font-weight: 600; }
45
+ .json-null { color: #f87171; font-style: italic; }
46
+ .json-preview {
47
+ color: #64748b;
48
+ font-style: italic;
49
+ margin: 0 4px;
50
+ cursor: pointer;
51
+ }
52
+ .json-preview:hover { color: #a5f3fc; }
53
+ `,ve=e.defineComponent({__name:"DevDebug",props:{data:{},title:{default:""},maxHeight:{default:"300px"},collapsed:{type:Boolean,default:!1},draggable:{type:Boolean,default:!1},forceShow:{type:Boolean,default:!1}},setup(o){const a=e.reactive({value:0}),s=e.reactive({value:0});e.provide("expandAll",a),e.provide("collapseAll",s);const y=()=>{a.value++},j=()=>{s.value++},t=o,x=e.inject("devDebugForceShow",{value:!1}),E=e.computed(()=>!!(t.forceShow||x.value)),p=e.ref(t.collapsed),r=e.ref(!1),m=e.ref(!1),c=e.ref(t.draggable),v=e.ref({x:20,y:20}),h=e.ref({x:0,y:0}),k=e.ref(null),i=e.ref(null),l=e.computed(()=>t.title?t.title:t.data&&typeof t.data=="object"&&!Array.isArray(t.data)&&Object.keys(t.data).length===1?Object.keys(t.data)[0]:"Debug"),b=e.computed(()=>t.data&&typeof t.data=="object"&&!Array.isArray(t.data)&&Object.keys(t.data).length===1&&!t.title?Object.values(t.data)[0]:t.data),_=e.computed(()=>{try{return JSON.stringify(b.value,null,2)}catch{return String(b.value)}}),N=()=>{p.value=!p.value},F=()=>{r.value=!r.value},O=()=>{c.value=!c.value,c.value&&(v.value={x:20,y:20})},z=n=>{!c.value||r.value||(m.value=!0,h.value={x:n.clientX-v.value.x,y:n.clientY-v.value.y},n.preventDefault())},V=n=>{m.value&&(v.value={x:Math.max(0,Math.min(window.innerWidth-300,n.clientX-h.value.x)),y:Math.max(0,Math.min(window.innerHeight-100,n.clientY-h.value.y))})},C=()=>{m.value=!1};e.onMounted(()=>{document.addEventListener("mousemove",V),document.addEventListener("mouseup",C)}),e.onUnmounted(()=>{document.removeEventListener("mousemove",V),document.removeEventListener("mouseup",C)});const w=(n,g="",T=!0)=>{const d=T?"":'<span class="json-comma">,</span>',u=g?`<span class="json-key">"${g}"</span><span class="json-colon">: </span>`:"";if(n===null)return`<div class="json-line">${u}<span class="json-value json-null">null</span>${d}</div>`;if(Array.isArray(n)){if(n.length===0)return`<div class="json-line">${u}<span class="json-bracket">[]</span>${d}</div>`;const f=n.map((D,$)=>w(D,"",$===n.length-1)).join("");return`
54
+ <div class="json-node">
55
+ <div class="json-line">${u}<span class="json-bracket">[</span></div>
56
+ <div class="json-children">${f}</div>
57
+ <div class="json-line"><span class="json-bracket">]</span>${d}</div>
58
+ </div>
59
+ `}if(typeof n=="object"){const f=Object.entries(n);if(f.length===0)return`<div class="json-line">${u}<span class="json-bracket">{}</span>${d}</div>`;const D=f.map(([$,I],P)=>w(I,$,P===f.length-1)).join("");return`
60
+ <div class="json-node">
61
+ <div class="json-line">${u}<span class="json-bracket">{</span></div>
62
+ <div class="json-children">${D}</div>
63
+ <div class="json-line"><span class="json-bracket">}</span>${d}</div>
64
+ </div>
65
+ `}if(typeof n=="string"){const f=n.replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");return`<div class="json-line">${u}<span class="json-value json-string">"${f}"</span>${d}</div>`}return typeof n=="number"?`<div class="json-line">${u}<span class="json-value json-number">${n}</span>${d}</div>`:typeof n=="boolean"?`<div class="json-line">${u}<span class="json-value json-boolean">${n}</span>${d}</div>`:`<div class="json-line">${u}<span class="json-value">${String(n)}</span>${d}</div>`},L=()=>{const n=window.open("","_blank","width=800,height=600");if(n){const g=w(b.value);n.document.write(`
66
+ <!DOCTYPE html>
67
+ <html>
68
+ <head>
69
+ <title>${l.value}</title>
70
+ <style>${ue}</style>
71
+ </head>
72
+ <body>
73
+ <h1>🔍 ${l.value}</h1>
74
+ <div class="json-tree">${g}</div>
75
+ </body>
76
+ </html>
77
+ `),n.document.close()}},J=async()=>{try{await navigator.clipboard.writeText(_.value)}catch{console.error("Failed to copy to clipboard")}},H=e.computed(()=>!c.value||r.value?{}:{position:"fixed",left:`${v.value.x}px`,top:`${v.value.y}px`,zIndex:99998,width:"400px",height:"300px",maxWidth:"calc(100vw - 40px)",maxHeight:"calc(100vh - 40px)"});return(n,g)=>E.value?(e.openBlock(),e.createElementBlock("div",{key:0,ref_key:"debugEl",ref:k,class:e.normalizeClass(["dev-debug",{"dev-debug--fullscreen":r.value,"dev-debug--floating":c.value&&!r.value,"dev-debug--dragging":m.value}]),style:e.normalizeStyle(H.value)},[e.createElementVNode("div",{class:e.normalizeClass(["dev-debug__header",{"dev-debug__header--draggable":c.value&&!r.value}]),onMousedown:z},[e.createElementVNode("button",{class:"dev-debug__toggle",onClick:e.withModifiers(N,["stop"])},[e.createElementVNode("span",{class:e.normalizeClass(["dev-debug__arrow",{"dev-debug__arrow--collapsed":p.value}])}," ▼ ",2)]),e.createElementVNode("span",re,"🔍 "+e.toDisplayString(l.value),1),e.createElementVNode("div",ce,[e.createElementVNode("button",{class:"dev-debug__btn",title:"Expand all",onClick:e.withModifiers(y,["stop"])},"⊕"),e.createElementVNode("button",{class:"dev-debug__btn",title:"Collapse all",onClick:e.withModifiers(j,["stop"])},"⊖"),g[0]||(g[0]=e.createElementVNode("span",{class:"dev-debug__separator"},null,-1)),e.createElementVNode("button",{class:"dev-debug__btn",title:"Copy",onClick:e.withModifiers(J,["stop"])},"📋"),e.createElementVNode("button",{class:"dev-debug__btn",title:"Open in new window",onClick:e.withModifiers(L,["stop"])}," ↗️ "),e.createElementVNode("button",{class:"dev-debug__btn",title:c.value?"Dock":"Float",onClick:e.withModifiers(O,["stop"])},e.toDisplayString(c.value?"📌":"🪟"),9,ie),e.createElementVNode("button",{class:"dev-debug__btn",title:"Fullscreen",onClick:e.withModifiers(F,["stop"])},e.toDisplayString(r.value?"⊙":"⛶"),1)])],34),e.withDirectives(e.createElementVNode("div",{ref_key:"contentEl",ref:i,class:"dev-debug__content",style:e.normalizeStyle({maxHeight:r.value||c.value?"none":o.maxHeight})},[e.createElementVNode("div",de,[e.createVNode(B,{data:b.value,root:!0},null,8,["data"])])],4),[[e.vShow,!p.value]])],6)):e.createCommentVNode("",!0)}}),A=S(ve,[["__scopeId","data-v-8fdf8728"]]),M={install(o,a={}){const s=a.componentName||"DevDebug";o.component(s,A),a.forceShow&&o.provide("devDebugForceShow",{value:!0})}};exports.DevDebug=A;exports.DevDebugJsonNode=B;exports.VueDevDebugPlugin=M;exports.default=M;
@@ -0,0 +1,66 @@
1
+ import { ComponentOptionsMixin } from 'vue';
2
+ import { ComponentProvideOptions } from 'vue';
3
+ import { DefineComponent } from 'vue';
4
+ import { Plugin as Plugin_2 } from 'vue';
5
+ import { PublicProps } from 'vue';
6
+
7
+ declare type __VLS_Props = {
8
+ data: unknown;
9
+ title?: string;
10
+ maxHeight?: string;
11
+ collapsed?: boolean;
12
+ draggable?: boolean;
13
+ forceShow?: boolean;
14
+ };
15
+
16
+ declare type __VLS_Props_2 = {
17
+ data: unknown;
18
+ keyName?: string;
19
+ root?: boolean;
20
+ isLast?: boolean;
21
+ };
22
+
23
+ export declare const DevDebug: DefineComponent<__VLS_Props, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
24
+ title: string;
25
+ maxHeight: string;
26
+ collapsed: boolean;
27
+ draggable: boolean;
28
+ forceShow: boolean;
29
+ }, {}, {}, {}, string, ComponentProvideOptions, false, {
30
+ debugEl: HTMLDivElement;
31
+ contentEl: HTMLDivElement;
32
+ }, any>;
33
+
34
+ export declare const DevDebugJsonNode: DefineComponent<__VLS_Props_2, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, PublicProps, Readonly<__VLS_Props_2> & Readonly<{}>, {
35
+ keyName: string;
36
+ root: boolean;
37
+ isLast: boolean;
38
+ }, {}, {}, {}, string, ComponentProvideOptions, false, {}, any>;
39
+
40
+ export declare interface DevDebugProps {
41
+ /** Data to display in the debug viewer */
42
+ data: unknown;
43
+ /** Title for the debug panel (auto-extracted from object key if not provided) */
44
+ title?: string;
45
+ /** Maximum height of the content area */
46
+ maxHeight?: string;
47
+ /** Start collapsed */
48
+ collapsed?: boolean;
49
+ /** Enable draggable floating mode */
50
+ draggable?: boolean;
51
+ /** Force show even in production (use with caution!) */
52
+ forceShow?: boolean;
53
+ }
54
+
55
+ declare const VueDevDebugPlugin: Plugin_2;
56
+ export { VueDevDebugPlugin }
57
+ export default VueDevDebugPlugin;
58
+
59
+ export declare interface VueDevDebugPluginOptions {
60
+ /** Force show debug panels in production (not recommended) */
61
+ forceShow?: boolean;
62
+ /** Custom component name (default: 'DevDebug') */
63
+ componentName?: string;
64
+ }
65
+
66
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,370 @@
1
+ import { defineComponent as P, ref as f, inject as H, watch as V, computed as l, resolveComponent as se, createElementBlock as p, openBlock as d, createCommentVNode as x, createElementVNode as n, Fragment as F, toDisplayString as v, normalizeClass as O, renderList as te, createBlock as ae, reactive as I, provide as W, onMounted as oe, onUnmounted as le, normalizeStyle as Y, withDirectives as re, withModifiers as k, createVNode as ce, vShow as ie } from "vue";
2
+ const ue = {
3
+ key: 0,
4
+ class: "json-line"
5
+ }, de = { class: "json-key" }, ve = {
6
+ key: 1,
7
+ class: "json-comma"
8
+ }, pe = {
9
+ key: 1,
10
+ class: "json-line json-expandable"
11
+ }, ge = { class: "json-key" }, be = { class: "json-bracket" }, fe = { class: "json-bracket" }, je = {
12
+ key: 1,
13
+ class: "json-comma"
14
+ }, me = {
15
+ key: 2,
16
+ class: "json-node"
17
+ }, ye = { class: "json-line json-expandable" }, he = { class: "json-key" }, _e = { class: "json-bracket" }, ke = { class: "json-children" }, xe = { class: "json-line" }, we = { class: "json-bracket" }, $e = {
18
+ key: 0,
19
+ class: "json-comma"
20
+ }, De = /* @__PURE__ */ P({
21
+ __name: "DevDebugJsonNode",
22
+ props: {
23
+ data: {},
24
+ keyName: { default: "" },
25
+ root: { type: Boolean, default: !1 },
26
+ isLast: { type: Boolean, default: !0 }
27
+ },
28
+ setup(t) {
29
+ const a = t, r = f(!0), $ = H("expandAll", { value: 0 }), D = H("collapseAll", { value: 0 });
30
+ V(
31
+ () => $.value,
32
+ () => {
33
+ r.value = !0;
34
+ }
35
+ ), V(
36
+ () => D.value,
37
+ () => {
38
+ r.value = !1;
39
+ }
40
+ );
41
+ const e = l(() => a.data === null ? "null" : Array.isArray(a.data) ? "array" : typeof a.data), N = l(() => e.value === "object" || e.value === "array"), E = l(() => e.value === "array" ? a.data.length : e.value === "object" ? Object.keys(a.data).length : 0), m = l(() => e.value === "array" ? a.data.map((u, o) => ({ key: String(o), value: u })) : e.value === "object" ? Object.entries(a.data).map(([u, o]) => ({ key: u, value: o })) : []), c = l(() => e.value === "array" ? "[" : "{"), y = l(() => e.value === "array" ? "]" : "}"), i = l(() => e.value === "array" ? `[ ${E.value} items ]` : `{ ${E.value} keys }`), j = l(() => e.value === "string" ? `"${a.data}"` : e.value === "null" ? "null" : String(a.data)), C = l(() => {
42
+ const u = "json-value";
43
+ switch (e.value) {
44
+ case "string":
45
+ return `${u} json-string`;
46
+ case "number":
47
+ return `${u} json-number`;
48
+ case "boolean":
49
+ return `${u} json-boolean`;
50
+ case "null":
51
+ return `${u} json-null`;
52
+ default:
53
+ return u;
54
+ }
55
+ }), S = () => {
56
+ N.value && (r.value = !r.value);
57
+ };
58
+ return (u, o) => {
59
+ const w = se("DevDebugJsonNode", !0);
60
+ return N.value ? r.value ? (d(), p("div", me, [
61
+ n("div", ye, [
62
+ n("span", {
63
+ class: "json-toggle json-toggle--expanded",
64
+ onClick: S
65
+ }, "▼"),
66
+ t.keyName ? (d(), p(F, { key: 0 }, [
67
+ n("span", he, '"' + v(t.keyName) + '"', 1),
68
+ o[2] || (o[2] = n("span", { class: "json-colon" }, ": ", -1))
69
+ ], 64)) : x("", !0),
70
+ n("span", _e, v(c.value), 1)
71
+ ]),
72
+ n("div", ke, [
73
+ (d(!0), p(F, null, te(m.value, (A, B) => (d(), ae(w, {
74
+ key: A.key,
75
+ data: A.value,
76
+ "key-name": e.value === "array" ? "" : A.key,
77
+ "is-last": B === m.value.length - 1
78
+ }, null, 8, ["data", "key-name", "is-last"]))), 128))
79
+ ]),
80
+ n("div", xe, [
81
+ n("span", we, v(y.value), 1),
82
+ t.isLast ? x("", !0) : (d(), p("span", $e, ","))
83
+ ])
84
+ ])) : (d(), p("div", pe, [
85
+ n("span", {
86
+ class: "json-toggle",
87
+ onClick: S
88
+ }, "▶"),
89
+ t.keyName ? (d(), p(F, { key: 0 }, [
90
+ n("span", ge, '"' + v(t.keyName) + '"', 1),
91
+ o[1] || (o[1] = n("span", { class: "json-colon" }, ": ", -1))
92
+ ], 64)) : x("", !0),
93
+ n("span", be, v(c.value), 1),
94
+ n("span", {
95
+ class: "json-preview",
96
+ onClick: S
97
+ }, v(i.value), 1),
98
+ n("span", fe, v(y.value), 1),
99
+ t.isLast ? x("", !0) : (d(), p("span", je, ","))
100
+ ])) : (d(), p("div", ue, [
101
+ t.keyName ? (d(), p(F, { key: 0 }, [
102
+ n("span", de, '"' + v(t.keyName) + '"', 1),
103
+ o[0] || (o[0] = n("span", { class: "json-colon" }, ": ", -1))
104
+ ], 64)) : x("", !0),
105
+ n("span", {
106
+ class: O(C.value)
107
+ }, v(j.value), 3),
108
+ t.isLast ? x("", !0) : (d(), p("span", ve, ","))
109
+ ]));
110
+ };
111
+ }
112
+ }), X = (t, a) => {
113
+ const r = t.__vccOpts || t;
114
+ for (const [$, D] of a)
115
+ r[$] = D;
116
+ return r;
117
+ }, Ce = /* @__PURE__ */ X(De, [["__scopeId", "data-v-2d2f26de"]]), Se = { class: "dev-debug__title" }, Ae = { class: "dev-debug__actions" }, Ne = ["title"], Ee = { class: "json-tree" }, Fe = `
118
+ * { margin: 0; padding: 0; box-sizing: border-box; }
119
+ body {
120
+ font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
121
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
122
+ color: #a5f3fc;
123
+ padding: 20px;
124
+ min-height: 100vh;
125
+ }
126
+ h1 {
127
+ color: #f472b6;
128
+ margin-bottom: 16px;
129
+ font-size: 16px;
130
+ text-transform: uppercase;
131
+ letter-spacing: 0.5px;
132
+ }
133
+ .json-tree {
134
+ line-height: 1.6;
135
+ font-size: 13px;
136
+ }
137
+ .json-line { white-space: nowrap; }
138
+ .json-toggle {
139
+ display: inline-block;
140
+ width: 16px;
141
+ color: #64748b;
142
+ cursor: pointer;
143
+ user-select: none;
144
+ font-size: 10px;
145
+ }
146
+ .json-toggle:hover { color: #f472b6; }
147
+ .json-toggle--expanded { color: #a5f3fc; }
148
+ .json-children {
149
+ padding-left: 20px;
150
+ border-left: 1px solid #3b3b5c;
151
+ margin-left: 7px;
152
+ }
153
+ .json-key { color: #c084fc; font-weight: 500; }
154
+ .json-colon { color: #64748b; margin-right: 4px; }
155
+ .json-comma { color: #64748b; }
156
+ .json-bracket { color: #64748b; }
157
+ .json-value { color: #a5f3fc; }
158
+ .json-string { color: #6ee7b7; }
159
+ .json-number { color: #fbbf24; }
160
+ .json-boolean { color: #f472b6; font-weight: 600; }
161
+ .json-null { color: #f87171; font-style: italic; }
162
+ .json-preview {
163
+ color: #64748b;
164
+ font-style: italic;
165
+ margin: 0 4px;
166
+ cursor: pointer;
167
+ }
168
+ .json-preview:hover { color: #a5f3fc; }
169
+ `, Oe = /* @__PURE__ */ P({
170
+ __name: "DevDebug",
171
+ props: {
172
+ data: {},
173
+ title: { default: "" },
174
+ maxHeight: { default: "300px" },
175
+ collapsed: { type: Boolean, default: !1 },
176
+ draggable: { type: Boolean, default: !1 },
177
+ forceShow: { type: Boolean, default: !1 }
178
+ },
179
+ setup(t) {
180
+ const a = I({ value: 0 }), r = I({ value: 0 });
181
+ W("expandAll", a), W("collapseAll", r);
182
+ const $ = () => {
183
+ a.value++;
184
+ }, D = () => {
185
+ r.value++;
186
+ }, e = t, N = H("devDebugForceShow", { value: !1 }), E = l(() => !!(e.forceShow || N.value)), m = f(e.collapsed), c = f(!1), y = f(!1), i = f(e.draggable), j = f({ x: 20, y: 20 }), C = f({ x: 0, y: 0 }), S = f(null), u = f(null), o = l(() => e.title ? e.title : e.data && typeof e.data == "object" && !Array.isArray(e.data) && Object.keys(e.data).length === 1 ? Object.keys(e.data)[0] : "Debug"), w = l(() => e.data && typeof e.data == "object" && !Array.isArray(e.data) && Object.keys(e.data).length === 1 && !e.title ? Object.values(e.data)[0] : e.data), A = l(() => {
187
+ try {
188
+ return JSON.stringify(w.value, null, 2);
189
+ } catch {
190
+ return String(w.value);
191
+ }
192
+ }), B = () => {
193
+ m.value = !m.value;
194
+ }, q = () => {
195
+ c.value = !c.value;
196
+ }, U = () => {
197
+ i.value = !i.value, i.value && (j.value = { x: 20, y: 20 });
198
+ }, G = (s) => {
199
+ !i.value || c.value || (y.value = !0, C.value = {
200
+ x: s.clientX - j.value.x,
201
+ y: s.clientY - j.value.y
202
+ }, s.preventDefault());
203
+ }, J = (s) => {
204
+ y.value && (j.value = {
205
+ x: Math.max(0, Math.min(window.innerWidth - 300, s.clientX - C.value.x)),
206
+ y: Math.max(0, Math.min(window.innerHeight - 100, s.clientY - C.value.y))
207
+ });
208
+ }, T = () => {
209
+ y.value = !1;
210
+ };
211
+ oe(() => {
212
+ document.addEventListener("mousemove", J), document.addEventListener("mouseup", T);
213
+ }), le(() => {
214
+ document.removeEventListener("mousemove", J), document.removeEventListener("mouseup", T);
215
+ });
216
+ const L = (s, h = "", Z = !0) => {
217
+ const g = Z ? "" : '<span class="json-comma">,</span>', b = h ? `<span class="json-key">"${h}"</span><span class="json-colon">: </span>` : "";
218
+ if (s === null)
219
+ return `<div class="json-line">${b}<span class="json-value json-null">null</span>${g}</div>`;
220
+ if (Array.isArray(s)) {
221
+ if (s.length === 0)
222
+ return `<div class="json-line">${b}<span class="json-bracket">[]</span>${g}</div>`;
223
+ const _ = s.map((M, z) => L(M, "", z === s.length - 1)).join("");
224
+ return `
225
+ <div class="json-node">
226
+ <div class="json-line">${b}<span class="json-bracket">[</span></div>
227
+ <div class="json-children">${_}</div>
228
+ <div class="json-line"><span class="json-bracket">]</span>${g}</div>
229
+ </div>
230
+ `;
231
+ }
232
+ if (typeof s == "object") {
233
+ const _ = Object.entries(s);
234
+ if (_.length === 0)
235
+ return `<div class="json-line">${b}<span class="json-bracket">{}</span>${g}</div>`;
236
+ const M = _.map(([z, ee], ne) => L(ee, z, ne === _.length - 1)).join("");
237
+ return `
238
+ <div class="json-node">
239
+ <div class="json-line">${b}<span class="json-bracket">{</span></div>
240
+ <div class="json-children">${M}</div>
241
+ <div class="json-line"><span class="json-bracket">}</span>${g}</div>
242
+ </div>
243
+ `;
244
+ }
245
+ if (typeof s == "string") {
246
+ const _ = s.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
247
+ return `<div class="json-line">${b}<span class="json-value json-string">"${_}"</span>${g}</div>`;
248
+ }
249
+ return typeof s == "number" ? `<div class="json-line">${b}<span class="json-value json-number">${s}</span>${g}</div>` : typeof s == "boolean" ? `<div class="json-line">${b}<span class="json-value json-boolean">${s}</span>${g}</div>` : `<div class="json-line">${b}<span class="json-value">${String(s)}</span>${g}</div>`;
250
+ }, K = () => {
251
+ const s = window.open("", "_blank", "width=800,height=600");
252
+ if (s) {
253
+ const h = L(w.value);
254
+ s.document.write(`
255
+ <!DOCTYPE html>
256
+ <html>
257
+ <head>
258
+ <title>${o.value}</title>
259
+ <style>${Fe}</style>
260
+ </head>
261
+ <body>
262
+ <h1>🔍 ${o.value}</h1>
263
+ <div class="json-tree">${h}</div>
264
+ </body>
265
+ </html>
266
+ `), s.document.close();
267
+ }
268
+ }, Q = async () => {
269
+ try {
270
+ await navigator.clipboard.writeText(A.value);
271
+ } catch {
272
+ console.error("Failed to copy to clipboard");
273
+ }
274
+ }, R = l(() => !i.value || c.value ? {} : {
275
+ position: "fixed",
276
+ left: `${j.value.x}px`,
277
+ top: `${j.value.y}px`,
278
+ zIndex: 99998,
279
+ width: "400px",
280
+ height: "300px",
281
+ maxWidth: "calc(100vw - 40px)",
282
+ maxHeight: "calc(100vh - 40px)"
283
+ });
284
+ return (s, h) => E.value ? (d(), p("div", {
285
+ key: 0,
286
+ ref_key: "debugEl",
287
+ ref: S,
288
+ class: O(["dev-debug", {
289
+ "dev-debug--fullscreen": c.value,
290
+ "dev-debug--floating": i.value && !c.value,
291
+ "dev-debug--dragging": y.value
292
+ }]),
293
+ style: Y(R.value)
294
+ }, [
295
+ n("div", {
296
+ class: O(["dev-debug__header", { "dev-debug__header--draggable": i.value && !c.value }]),
297
+ onMousedown: G
298
+ }, [
299
+ n("button", {
300
+ class: "dev-debug__toggle",
301
+ onClick: k(B, ["stop"])
302
+ }, [
303
+ n("span", {
304
+ class: O(["dev-debug__arrow", { "dev-debug__arrow--collapsed": m.value }])
305
+ }, " ▼ ", 2)
306
+ ]),
307
+ n("span", Se, "🔍 " + v(o.value), 1),
308
+ n("div", Ae, [
309
+ n("button", {
310
+ class: "dev-debug__btn",
311
+ title: "Expand all",
312
+ onClick: k($, ["stop"])
313
+ }, "⊕"),
314
+ n("button", {
315
+ class: "dev-debug__btn",
316
+ title: "Collapse all",
317
+ onClick: k(D, ["stop"])
318
+ }, "⊖"),
319
+ h[0] || (h[0] = n("span", { class: "dev-debug__separator" }, null, -1)),
320
+ n("button", {
321
+ class: "dev-debug__btn",
322
+ title: "Copy",
323
+ onClick: k(Q, ["stop"])
324
+ }, "📋"),
325
+ n("button", {
326
+ class: "dev-debug__btn",
327
+ title: "Open in new window",
328
+ onClick: k(K, ["stop"])
329
+ }, " ↗️ "),
330
+ n("button", {
331
+ class: "dev-debug__btn",
332
+ title: i.value ? "Dock" : "Float",
333
+ onClick: k(U, ["stop"])
334
+ }, v(i.value ? "📌" : "🪟"), 9, Ne),
335
+ n("button", {
336
+ class: "dev-debug__btn",
337
+ title: "Fullscreen",
338
+ onClick: k(q, ["stop"])
339
+ }, v(c.value ? "⊙" : "⛶"), 1)
340
+ ])
341
+ ], 34),
342
+ re(n("div", {
343
+ ref_key: "contentEl",
344
+ ref: u,
345
+ class: "dev-debug__content",
346
+ style: Y({ maxHeight: c.value || i.value ? "none" : t.maxHeight })
347
+ }, [
348
+ n("div", Ee, [
349
+ ce(Ce, {
350
+ data: w.value,
351
+ root: !0
352
+ }, null, 8, ["data"])
353
+ ])
354
+ ], 4), [
355
+ [ie, !m.value]
356
+ ])
357
+ ], 6)) : x("", !0);
358
+ }
359
+ }), Be = /* @__PURE__ */ X(Oe, [["__scopeId", "data-v-8fdf8728"]]), Me = {
360
+ install(t, a = {}) {
361
+ const r = a.componentName || "DevDebug";
362
+ t.component(r, Be), a.forceShow && t.provide("devDebugForceShow", { value: !0 });
363
+ }
364
+ };
365
+ export {
366
+ Be as DevDebug,
367
+ Ce as DevDebugJsonNode,
368
+ Me as VueDevDebugPlugin,
369
+ Me as default
370
+ };
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .json-line[data-v-2d2f26de]{white-space:nowrap}.json-expandable[data-v-2d2f26de]{cursor:default}.json-toggle[data-v-2d2f26de]{display:inline-block;width:16px;color:#64748b;cursor:pointer;-webkit-user-select:none;user-select:none;font-size:10px;transition:color .15s}.json-toggle[data-v-2d2f26de]:hover{color:#f472b6}.json-toggle--expanded[data-v-2d2f26de]{color:#a5f3fc}.json-node[data-v-2d2f26de]{display:contents}.json-children[data-v-2d2f26de]{padding-left:20px;border-left:1px solid #3b3b5c;margin-left:7px}.json-key[data-v-2d2f26de]{color:#c084fc;font-weight:500}.json-colon[data-v-2d2f26de]{color:#64748b;margin-right:4px}.json-comma[data-v-2d2f26de],.json-bracket[data-v-2d2f26de]{color:#64748b}.json-value[data-v-2d2f26de]{color:#a5f3fc}.json-string[data-v-2d2f26de]{color:#6ee7b7}.json-number[data-v-2d2f26de]{color:#fbbf24}.json-boolean[data-v-2d2f26de]{color:#f472b6;font-weight:600}.json-null[data-v-2d2f26de]{color:#f87171;font-style:italic}.json-preview[data-v-2d2f26de]{color:#64748b;font-style:italic;margin:0 4px;cursor:pointer;transition:color .15s}.json-preview[data-v-2d2f26de]:hover{color:#a5f3fc}.dev-debug[data-v-8fdf8728]{font-family:JetBrains Mono,Fira Code,Consolas,monospace;background:linear-gradient(135deg,#1a1a2e,#16213e);border:1px solid #f472b6;border-radius:8px;overflow:hidden;box-shadow:0 4px 20px #f472b626,inset 0 1px #ffffff0d;font-size:13px;margin:8px 0}.dev-debug--floating[data-v-8fdf8728]{box-shadow:0 8px 32px #0006,0 4px 20px #f472b640;resize:both;min-width:300px;min-height:100px;display:flex;flex-direction:column}.dev-debug--dragging[data-v-8fdf8728]{opacity:.9;cursor:grabbing!important}.dev-debug--fullscreen[data-v-8fdf8728]{position:fixed;top:16px;right:16px;bottom:16px;left:16px;z-index:99999;margin:0;border-radius:12px;width:auto!important;max-width:none!important;height:auto!important;display:flex;flex-direction:column}.dev-debug__header[data-v-8fdf8728]{display:flex;align-items:center;gap:8px;padding:8px 12px;background:linear-gradient(90deg,#2d1b4e,#1e3a5f);border-bottom:1px solid #3b3b5c;flex-shrink:0}.dev-debug__header--draggable[data-v-8fdf8728]{cursor:grab}.dev-debug__header--draggable[data-v-8fdf8728]:active{cursor:grabbing}.dev-debug__toggle[data-v-8fdf8728]{background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center}.dev-debug__arrow[data-v-8fdf8728]{color:#f472b6;font-size:10px;transition:transform .2s ease;display:inline-block}.dev-debug__arrow--collapsed[data-v-8fdf8728]{transform:rotate(-90deg)}.dev-debug__title[data-v-8fdf8728]{color:#f472b6;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.5px}.dev-debug__actions[data-v-8fdf8728]{margin-left:auto;display:flex;gap:4px}.dev-debug__btn[data-v-8fdf8728]{background:#ffffff0d;border:1px solid rgba(255,255,255,.1);border-radius:4px;padding:4px 8px;cursor:pointer;font-size:12px;transition:all .15s ease}.dev-debug__btn[data-v-8fdf8728]:hover{background:#f472b633;border-color:#f472b6}.dev-debug__separator[data-v-8fdf8728]{width:1px;height:16px;background:#3b3b5c;margin:0 4px}.dev-debug__content[data-v-8fdf8728]{overflow:auto;scrollbar-width:thin;scrollbar-color:#f472b6 #1a1a2e}.dev-debug--floating .dev-debug__content[data-v-8fdf8728],.dev-debug--fullscreen .dev-debug__content[data-v-8fdf8728]{flex:1;min-height:0}.dev-debug__content[data-v-8fdf8728]::-webkit-scrollbar{width:8px;height:8px}.dev-debug__content[data-v-8fdf8728]::-webkit-scrollbar-track{background:#1a1a2e}.dev-debug__content[data-v-8fdf8728]::-webkit-scrollbar-thumb{background:#f472b6;border-radius:4px}.json-tree[data-v-8fdf8728]{padding:12px 16px;line-height:1.6}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@vahy_the/vue-dev-debug",
3
+ "version": "0.1.0",
4
+ "description": "Beautiful JSON debug viewer component for Vue 3 - only renders in development mode",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ },
16
+ "./style.css": "./dist/style.css"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md",
21
+ "LICENSE"
22
+ ],
23
+ "scripts": {
24
+ "dev": "vite",
25
+ "build": "vue-tsc -p tsconfig.json && vite build",
26
+ "preview": "vite preview",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "keywords": [
30
+ "vue",
31
+ "vue3",
32
+ "debug",
33
+ "devtools",
34
+ "json",
35
+ "viewer",
36
+ "developer",
37
+ "component"
38
+ ],
39
+ "peerDependencies": {
40
+ "vue": "^3.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "@vitejs/plugin-vue": "^5.2.1",
44
+ "typescript": "~5.6.0",
45
+ "vite": "^6.0.0",
46
+ "vite-plugin-dts": "^4.3.0",
47
+ "vue": "^3.5.13",
48
+ "vue-tsc": "^2.2.0"
49
+ }
50
+ }