react-looger 1.0.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/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # ReactLooger 🐞
2
+
3
+ A professional, high-performance visual logging system for Ionic React applications. Monitor logs, errors, and JSON objects in real-time with a beautiful floating UI.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Three Log Levels**: Debug, Error, and Object.
8
+ - 🐞 **Floating UI**: Draggable, minimizable, and responsive button.
9
+ - 📦 **JSON Tree Viewer**: Expandable visualization for complex objects.
10
+ - 💾 **Persistence**: Option to store logs in `localStorage` or `IndexedDB`.
11
+ - 🔍 **Search & Filters**: Quickly find specific logs or filter by level.
12
+ - 📥 **Export**: Download logs as JSON files.
13
+ - 🌓 **Dark Mode**: Automatic support for system theme.
14
+ - 🚀 **Virtualized List**: Handles thousands of logs without performance loss.
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install react-looger
20
+ # or
21
+ yarn add react-looger
22
+ ```
23
+
24
+ ## Setup
25
+
26
+ Wrap your application with the `LoggerProvider` and add the `LoggerViewer` component (ideally in your `App.tsx`).
27
+
28
+ ```tsx
29
+ import { LoggerProvider, LoggerViewer } from 'react-looger';
30
+
31
+ const App: React.FC = () => (
32
+ <LoggerProvider config={{ persistence: true, maxLogs: 1000 }}>
33
+ <IonApp>
34
+ {/* Your app components */}
35
+ <LoggerViewer />
36
+ </IonApp>
37
+ </LoggerProvider>
38
+ );
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ Use the `useLogger` hook anywhere in your components.
44
+
45
+ ```tsx
46
+ import { useLogger } from 'react-looger';
47
+
48
+ const MyComponent: React.FC = () => {
49
+ const { debug, error, object } = useLogger();
50
+
51
+ const handleAction = () => {
52
+ debug('Process started');
53
+
54
+ try {
55
+ const userData = { id: 1, name: 'John Doe', roles: ['admin', 'user'] };
56
+ object(userData, 'User Data');
57
+ } catch (e) {
58
+ error(e as Error);
59
+ }
60
+ };
61
+
62
+ return <button onClick={handleAction}>Run Action</button>;
63
+ };
64
+ ```
65
+
66
+ ### Configuration Options
67
+
68
+ | Option | Type | Default | Description |
69
+ | --- | --- | --- | --- |
70
+ | `persistence` | `boolean` | `false` | Enable log storage between sessions. |
71
+ | `persistenceDriver` | `'localStorage' \| 'indexedDB'` | `'localStorage'` | Choose where to store logs. |
72
+ | `maxLogs` | `number` | `500` | Maximum number of logs to keep. |
73
+ | `onLogAdded` | `(log: LogEntry) => void` | `undefined` | Callback fired every time a log is added. |
74
+
75
+ ## Local Development & Demo
76
+
77
+ To see the Liquid Glass logger in action within a sample Ionic React environment:
78
+
79
+ 1. **Build the Library**:
80
+ ```bash
81
+ npm run build
82
+ ```
83
+ 2. **Run the Demo**:
84
+ ```bash
85
+ cd example
86
+ npm install
87
+ npm run dev
88
+ ```
89
+ 3. **View**: Open `http://localhost:5173` in your browser.
90
+
91
+ ## API Reference
92
+
93
+ ### `useLogger()`
94
+ Returns:
95
+ - `debug(message: string)`: Log a debug message.
96
+ - `error(message: string | Error)`: Log an error or Error object.
97
+ - `object(obj: any, title?: string)`: Log a serializable object.
98
+ - `clear()`: Delete all current logs.
99
+ - `exportLogs()`: Get an array of all `LogEntry` objects.
100
+ - `logs`: Current array of logs.
101
+ - `unreadCount`: Number of logs added while panel was closed.
102
+
103
+ ## License
104
+
105
+ MIT
@@ -0,0 +1,130 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Supported severity levels for logging.
5
+ */
6
+ type LogLevel = 'DEBUG' | 'ERROR' | 'OBJECT';
7
+ /**
8
+ * Represents a single log entry in the system.
9
+ */
10
+ interface LogEntry {
11
+ /** Unique identifier for the log entry */
12
+ id: string;
13
+ /** The severity level of the log */
14
+ level: LogLevel;
15
+ /** Main descriptive message */
16
+ message: string;
17
+ /** Optional title for grouped logs or object labels */
18
+ title?: string;
19
+ /** ISO timestamp of when the log was created */
20
+ timestamp: string;
21
+ /** Optional raw data associated with the log (for OBJECT level) */
22
+ data?: any;
23
+ /** Optional stack trace (for ERROR level) */
24
+ stack?: string;
25
+ }
26
+ /**
27
+ * Configuration options for the Logger.
28
+ */
29
+ interface LoggerConfig {
30
+ /** Whether to persist logs between sessions. Default: false */
31
+ persistence?: boolean;
32
+ /** Choice of storage driver. Default: 'localStorage' */
33
+ persistenceDriver?: 'localStorage' | 'indexedDB';
34
+ /** Maximum number of logs to keep in memory/storage. Default: 500 */
35
+ maxLogs?: number;
36
+ /** Optional callback triggered on every new log */
37
+ onLogAdded?: (log: LogEntry) => void;
38
+ }
39
+ /**
40
+ * Internal interface for storage implementations.
41
+ */
42
+ interface StorageDriver {
43
+ save(logs: LogEntry[]): Promise<void>;
44
+ load(): Promise<LogEntry[]>;
45
+ clear(): Promise<void>;
46
+ }
47
+
48
+ /**
49
+ * Main hook to interact with the Liquid Glass Logger.
50
+ *
51
+ * Provides methods for different logging levels and allows accessing
52
+ * the current state of logs.
53
+ *
54
+ * @example
55
+ * ```tsx
56
+ * const { debug, error, object } = useLogger();
57
+ *
58
+ * debug('App started');
59
+ * error(new Error('Failed to fetch'), 'API Error');
60
+ * object({ user: 'John' }, 'Current User');
61
+ * ```
62
+ */
63
+ declare const useLogger: () => {
64
+ /** Register a debug message */
65
+ debug: (message: string) => void;
66
+ /** Register an error or exception */
67
+ error: (err: string | Error, title?: string) => void;
68
+ /** Register an object for JSON visualization */
69
+ object: (data: any, title?: string) => void;
70
+ /** Clear all history */
71
+ clear: () => void;
72
+ /** Download history as JSON */
73
+ exportLogs: () => void;
74
+ /** List of all captured logs */
75
+ logs: LogEntry[];
76
+ /** Count of logs not yet viewed */
77
+ unreadCount: number;
78
+ };
79
+
80
+ interface LoggerState {
81
+ logs: LogEntry[];
82
+ config: LoggerConfig;
83
+ unreadCount: number;
84
+ }
85
+ type LoggerAction = {
86
+ type: 'ADD_LOG';
87
+ log: LogEntry;
88
+ } | {
89
+ type: 'SET_LOGS';
90
+ logs: LogEntry[];
91
+ } | {
92
+ type: 'CLEAR_LOGS';
93
+ } | {
94
+ type: 'SET_CONFIG';
95
+ config: Partial<LoggerConfig>;
96
+ } | {
97
+ type: 'RESET_UNREAD';
98
+ } | {
99
+ type: 'INCREMENT_UNREAD';
100
+ };
101
+ interface LoggerContextType {
102
+ state: LoggerState;
103
+ dispatch: React.Dispatch<LoggerAction>;
104
+ isPanelOpen: boolean;
105
+ setIsPanelOpen: (open: boolean) => void;
106
+ }
107
+ /**
108
+ * Provides the logging context to all child components.
109
+ * This should wrap your entire application (e.g. in App.tsx).
110
+ *
111
+ * @param config Optional initial configuration for persistence and limits.
112
+ */
113
+ declare const LoggerProvider: React.FC<{
114
+ children: React.ReactNode;
115
+ config?: LoggerConfig;
116
+ }>;
117
+ /**
118
+ * Internal hook to access the logger context state.
119
+ * Use the public `useLogger` hook for standard logging operations.
120
+ */
121
+ declare const useLoggerContext: () => LoggerContextType;
122
+
123
+ /**
124
+ * Main entry point for the Logger UI.
125
+ * Renders the floating trigger button and conditionally shows the glass panel.
126
+ * Should be placed at the root level of your application.
127
+ */
128
+ declare const LoggerViewer: React.FC;
129
+
130
+ export { type LogEntry, type LogLevel, type LoggerConfig, LoggerProvider, LoggerViewer, type StorageDriver, useLogger, useLoggerContext };
@@ -0,0 +1,130 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Supported severity levels for logging.
5
+ */
6
+ type LogLevel = 'DEBUG' | 'ERROR' | 'OBJECT';
7
+ /**
8
+ * Represents a single log entry in the system.
9
+ */
10
+ interface LogEntry {
11
+ /** Unique identifier for the log entry */
12
+ id: string;
13
+ /** The severity level of the log */
14
+ level: LogLevel;
15
+ /** Main descriptive message */
16
+ message: string;
17
+ /** Optional title for grouped logs or object labels */
18
+ title?: string;
19
+ /** ISO timestamp of when the log was created */
20
+ timestamp: string;
21
+ /** Optional raw data associated with the log (for OBJECT level) */
22
+ data?: any;
23
+ /** Optional stack trace (for ERROR level) */
24
+ stack?: string;
25
+ }
26
+ /**
27
+ * Configuration options for the Logger.
28
+ */
29
+ interface LoggerConfig {
30
+ /** Whether to persist logs between sessions. Default: false */
31
+ persistence?: boolean;
32
+ /** Choice of storage driver. Default: 'localStorage' */
33
+ persistenceDriver?: 'localStorage' | 'indexedDB';
34
+ /** Maximum number of logs to keep in memory/storage. Default: 500 */
35
+ maxLogs?: number;
36
+ /** Optional callback triggered on every new log */
37
+ onLogAdded?: (log: LogEntry) => void;
38
+ }
39
+ /**
40
+ * Internal interface for storage implementations.
41
+ */
42
+ interface StorageDriver {
43
+ save(logs: LogEntry[]): Promise<void>;
44
+ load(): Promise<LogEntry[]>;
45
+ clear(): Promise<void>;
46
+ }
47
+
48
+ /**
49
+ * Main hook to interact with the Liquid Glass Logger.
50
+ *
51
+ * Provides methods for different logging levels and allows accessing
52
+ * the current state of logs.
53
+ *
54
+ * @example
55
+ * ```tsx
56
+ * const { debug, error, object } = useLogger();
57
+ *
58
+ * debug('App started');
59
+ * error(new Error('Failed to fetch'), 'API Error');
60
+ * object({ user: 'John' }, 'Current User');
61
+ * ```
62
+ */
63
+ declare const useLogger: () => {
64
+ /** Register a debug message */
65
+ debug: (message: string) => void;
66
+ /** Register an error or exception */
67
+ error: (err: string | Error, title?: string) => void;
68
+ /** Register an object for JSON visualization */
69
+ object: (data: any, title?: string) => void;
70
+ /** Clear all history */
71
+ clear: () => void;
72
+ /** Download history as JSON */
73
+ exportLogs: () => void;
74
+ /** List of all captured logs */
75
+ logs: LogEntry[];
76
+ /** Count of logs not yet viewed */
77
+ unreadCount: number;
78
+ };
79
+
80
+ interface LoggerState {
81
+ logs: LogEntry[];
82
+ config: LoggerConfig;
83
+ unreadCount: number;
84
+ }
85
+ type LoggerAction = {
86
+ type: 'ADD_LOG';
87
+ log: LogEntry;
88
+ } | {
89
+ type: 'SET_LOGS';
90
+ logs: LogEntry[];
91
+ } | {
92
+ type: 'CLEAR_LOGS';
93
+ } | {
94
+ type: 'SET_CONFIG';
95
+ config: Partial<LoggerConfig>;
96
+ } | {
97
+ type: 'RESET_UNREAD';
98
+ } | {
99
+ type: 'INCREMENT_UNREAD';
100
+ };
101
+ interface LoggerContextType {
102
+ state: LoggerState;
103
+ dispatch: React.Dispatch<LoggerAction>;
104
+ isPanelOpen: boolean;
105
+ setIsPanelOpen: (open: boolean) => void;
106
+ }
107
+ /**
108
+ * Provides the logging context to all child components.
109
+ * This should wrap your entire application (e.g. in App.tsx).
110
+ *
111
+ * @param config Optional initial configuration for persistence and limits.
112
+ */
113
+ declare const LoggerProvider: React.FC<{
114
+ children: React.ReactNode;
115
+ config?: LoggerConfig;
116
+ }>;
117
+ /**
118
+ * Internal hook to access the logger context state.
119
+ * Use the public `useLogger` hook for standard logging operations.
120
+ */
121
+ declare const useLoggerContext: () => LoggerContextType;
122
+
123
+ /**
124
+ * Main entry point for the Logger UI.
125
+ * Renders the floating trigger button and conditionally shows the glass panel.
126
+ * Should be placed at the root level of your application.
127
+ */
128
+ declare const LoggerViewer: React.FC;
129
+
130
+ export { type LogEntry, type LogLevel, type LoggerConfig, LoggerProvider, LoggerViewer, type StorageDriver, useLogger, useLoggerContext };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ 'use strict';var j=require('react'),idb=require('idb'),jsxRuntime=require('react/jsx-runtime'),reactVirtuoso=require('react-virtuoso');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var j__default=/*#__PURE__*/_interopDefault(j);function w(e,{insertAt:t}={}){if(typeof document>"u")return;let o=document.head||document.getElementsByTagName("head")[0],r=document.createElement("style");r.type="text/css",t==="top"&&o.firstChild?o.insertBefore(r,o.firstChild):o.appendChild(r),r.styleSheet?r.styleSheet.cssText=e:r.appendChild(document.createTextNode(e));}w(`:root{--liql-primary: #3498db;--liql-primary-glow: rgba(52, 152, 219, .5);--liql-error: #ff4d4d;--liql-error-glow: rgba(255, 77, 77, .4);--liql-object: #00ff88;--liql-object-glow: rgba(0, 255, 136, .4);--liql-glass-bg: rgba(15, 23, 42, .7);--liql-glass-border: rgba(255, 255, 255, .1);--liql-glass-edge: rgba(255, 255, 255, .05);--liql-text-main: #f8fafc;--liql-text-muted: #94a3b8;--liql-font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif}.liql-floatingButton{position:fixed;width:60px;height:60px;border-radius:50%;background:radial-gradient(circle at 30% 30%,rgba(255,255,255,.15),transparent),linear-gradient(135deg,var(--liql-primary),#2980b9);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);box-shadow:0 8px 32px #0006,0 0 15px var(--liql-primary-glow);display:flex;align-items:center;justify-content:center;cursor:grab;z-index:10000;border:1px solid var(--liql-glass-border);transition:all .4s cubic-bezier(.175,.885,.32,1.275);font-size:26px;color:#fff;user-select:none;touch-action:none}.liql-floatingButton:hover{transform:scale(1.1) rotate(5deg);box-shadow:0 12px 40px #00000080,0 0 25px var(--liql-primary-glow)}.liql-badge{position:absolute;top:-2px;right:-2px;background:var(--liql-error);color:#fff;border-radius:12px;min-width:20px;height:20px;padding:0 6px;font-size:11px;font-weight:700;display:flex;align-items:center;justify-content:center;border:2px solid #0f172a;box-shadow:0 0 10px var(--liql-error-glow)}.liql-panel{position:fixed;background:var(--liql-glass-bg);backdrop-filter:blur(25px) saturate(180%);-webkit-backdrop-filter:blur(25px) saturate(180%);box-shadow:0 20px 50px #0009,inset 0 0 0 1px var(--liql-glass-edge);border:1px solid var(--liql-glass-border);border-radius:20px;display:flex;flex-direction:column;z-index:10001;overflow:hidden;font-family:var(--liql-font-family);color:var(--liql-text-main);animation:liql-slideUp .4s cubic-bezier(.23,1,.32,1)}.liql-panelHeader{background:#ffffff0d;padding:16px 20px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--liql-glass-border)}.liql-title{font-weight:700;letter-spacing:.5px;text-transform:uppercase;font-size:12px;color:var(--liql-text-muted)}.liql-controls{display:flex;gap:12px}.liql-btnAction{background:#ffffff14;border:1px solid var(--liql-glass-border);color:var(--liql-text-main);width:32px;height:32px;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}.liql-btnAction:hover{background:#ffffff26;transform:translateY(-2px)}.liql-filterBar{padding:12px 20px;display:flex;gap:10px;background:#0003;align-items:center}.liql-searchInput{flex:1;background:#ffffff0d;border:1px solid var(--liql-glass-border);border-radius:10px;padding:8px 14px;color:#fff;font-size:13px}.liql-searchInput:focus{outline:none;border-color:var(--liql-primary);background:#ffffff14}.liql-selectFilter{background:#ffffff0d;border:1px solid var(--liql-glass-border);border-radius:10px;padding:8px;color:#fff;font-size:12px;cursor:pointer}.liql-logListContainer{flex:1;padding:10px}.liql-logItem{margin-bottom:10px;padding:16px;border-radius:14px;background:#ffffff08;border:1px solid var(--liql-glass-border);transition:all .3s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden}.liql-logItem:hover{background:#ffffff0f;border-color:#fff3;transform:scale(1.01)}.liql-logItem-debug{border-left:4px solid var(--liql-primary)}.liql-logItem-error{border-left:4px solid var(--liql-error);box-shadow:inset 0 0 15px var(--liql-error-glow)}.liql-logItem-object{border-left:4px solid var(--liql-object);box-shadow:inset 0 0 15px var(--liql-object-glow)}.liql-logMeta{display:flex;justify-content:space-between;margin-bottom:8px;font-size:11px;font-weight:500;text-transform:uppercase}.liql-statusTag{padding:2px 8px;border-radius:6px;font-size:9px;font-weight:700}.liql-tag-debug{background:#3498db33;color:#3498db}.liql-tag-error{background:#ff4d4d33;color:#ff4d4d}.liql-tag-object{background:#0f83;color:#0f8}.liql-logTime{color:var(--liql-text-muted)}.liql-logContent{font-size:14px;line-height:1.5;color:#e2e8f0}.liql-expandedContent{margin-top:12px;padding:12px;background:#0000004d;border-radius:10px;font-family:JetBrains Mono,monospace;font-size:12px;border:1px solid var(--liql-glass-border);color:#a5f3fc;overflow-x:auto}.liql-footerActions{padding:14px 20px;background:#0003;display:flex;justify-content:space-between;align-items:center;border-top:1px solid var(--liql-glass-border)}.liql-storageInfo{font-size:10px;color:var(--liql-text-muted)}.liql-btnClose{background:linear-gradient(135deg,var(--liql-primary),#2980b9);border:none;padding:8px 16px;border-radius:8px;color:#fff;font-weight:600;font-size:13px;cursor:pointer;box-shadow:0 4px 12px var(--liql-primary-glow)}@keyframes liql-slideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}@media(max-width:600px){.liql-panel{width:94%!important;height:85%!important;bottom:3%!important;right:3%!important;left:3%!important;border-radius:24px}}
2
+ `);var L=class{key;constructor(t="ionic_react_logger_logs"){this.key=t;}async save(t){try{localStorage.setItem(this.key,JSON.stringify(t));}catch(o){console.error("Failed to save logs to localStorage",o);}}async load(){try{let t=localStorage.getItem(this.key);return t?JSON.parse(t):[]}catch(t){return console.error("Failed to load logs from localStorage",t),[]}}async clear(){localStorage.removeItem(this.key);}};var _="IonicReactLoggerDB",x="logs",M=1,q=class{dbPromise;constructor(){this.dbPromise=idb.openDB(_,M,{upgrade(t){t.objectStoreNames.contains(x)||t.createObjectStore(x,{keyPath:"id"});}});}async save(t){let r=(await this.dbPromise).transaction(x,"readwrite"),i=r.objectStore(x);await i.clear();for(let l of t)await i.put(l);await r.done;}async load(){return (await this.dbPromise).getAll(x)}async clear(){let o=(await this.dbPromise).transaction(x,"readwrite");await o.objectStore(x).clear(),await o.done;}};function C(e){return e==="indexedDB"?new q:new L}var R={logs:[],config:{persistence:false,persistenceDriver:"localStorage",maxLogs:500},unreadCount:0};function J(e,t){switch(t.type){case "ADD_LOG":{let o=[t.log,...e.logs].slice(0,e.config.maxLogs);return {...e,logs:o}}case "SET_LOGS":return {...e,logs:t.logs};case "CLEAR_LOGS":return {...e,logs:[],unreadCount:0};case "SET_CONFIG":return {...e,config:{...e.config,...t.config}};case "RESET_UNREAD":return {...e,unreadCount:0};case "INCREMENT_UNREAD":return {...e,unreadCount:e.unreadCount+1};default:return e}}var k=j.createContext(void 0),xe=({children:e,config:t})=>{let[o,r]=j.useReducer(J,{...R,config:{...R.config,...t}}),[i,l]=j__default.default.useState(false),s=j.useRef(null);return j.useEffect(()=>{o.config.persistence&&(s.current=C(o.config.persistenceDriver||"localStorage"),s.current.load().then(c=>{r({type:"SET_LOGS",logs:c});}));},[o.config.persistence,o.config.persistenceDriver]),j.useEffect(()=>{o.config.persistence&&s.current&&s.current.save(o.logs);},[o.logs,o.config.persistence]),jsxRuntime.jsx(k.Provider,{value:{state:o,dispatch:r,isPanelOpen:i,setIsPanelOpen:l},children:e})},m=()=>{let e=j.useContext(k);if(!e)throw new Error("useLoggerContext must be used within a LoggerProvider");return e};var Le=()=>{let{state:e,dispatch:t,isPanelOpen:o}=m(),r=j.useCallback((a,n,g)=>{let f={id:Math.random().toString(36).substring(2,9),level:a,message:n,timestamp:new Date().toISOString(),...g};t({type:"ADD_LOG",log:f}),e.config.onLogAdded?.(f);},[t,e.config]),i=j.useCallback(a=>r("DEBUG",a),[r]),l=j.useCallback((a,n)=>{let g=a instanceof Error?a.message:a,f=a instanceof Error?a.stack:void 0;r("ERROR",g,{title:n||"Error",stack:f});},[r]),s=j.useCallback((a,n)=>{r("OBJECT","Object visualization",{title:n||"Data Object",data:a});},[r]),c=j.useCallback(()=>{t({type:"CLEAR_LOGS"});},[t]),p=j.useCallback(()=>{let a="data:text/json;charset=utf-8,"+encodeURIComponent(JSON.stringify(e.logs,null,2)),n=document.createElement("a");n.setAttribute("href",a),n.setAttribute("download",`logs_${new Date().getTime()}.json`),document.body.appendChild(n),n.click(),n.remove();},[e.logs]);return {debug:i,error:l,object:s,clear:c,exportLogs:p,logs:e.logs,unreadCount:e.unreadCount}};var D=()=>{let{state:e,setIsPanelOpen:t}=m(),[o,r]=j.useState({x:window.innerWidth-80,y:window.innerHeight-80}),i=j.useRef(false),l=j.useRef({x:0,y:0}),s=n=>{i.current=true,l.current={x:n.clientX-o.x,y:n.clientY-o.y};},c=n=>{i.current=true;let g=n.touches[0];l.current={x:g.clientX-o.x,y:g.clientY-o.y};},p=j.useCallback((n,g)=>{i.current&&r({x:Math.max(0,Math.min(window.innerWidth-60,n-l.current.x)),y:Math.max(0,Math.min(window.innerHeight-60,g-l.current.y))});},[]);j.useEffect(()=>{let n=y=>p(y.clientX,y.clientY),g=y=>p(y.touches[0].clientX,y.touches[0].clientY),f=()=>i.current=false;return window.addEventListener("mousemove",n),window.addEventListener("touchmove",g),window.addEventListener("mouseup",f),window.addEventListener("touchend",f),()=>{window.removeEventListener("mousemove",n),window.removeEventListener("touchmove",g),window.removeEventListener("mouseup",f),window.removeEventListener("touchend",f);}},[p]);let a=()=>{i.current||t(true);};return jsxRuntime.jsxs("div",{className:"liql-floatingButton",style:{left:`${o.x}px`,top:`${o.y}px`},onMouseDown:s,onTouchStart:c,onClick:a,"aria-label":"Open Logger",children:["\u{1F41E}",e.unreadCount>0&&jsxRuntime.jsx("div",{className:"liql-badge",children:e.unreadCount})]})};var O=({log:e})=>{let[t,o]=j.useState(false),r=c=>new Date(c).toTimeString().split(" ")[0],i=c=>{try{return jsxRuntime.jsx("pre",{className:"liql-expandedContent",children:JSON.stringify(c,null,2)})}catch{return jsxRuntime.jsx("div",{children:"Error parsing object"})}},l=c=>{switch(c){case "DEBUG":return "\u{1F41E}";case "ERROR":return "\u274C";case "OBJECT":return "\u{1F4E6}";default:return "\u{1F4DD}"}},s=e.level.toLowerCase();return jsxRuntime.jsxs("div",{className:`liql-logItem liql-logItem-${s}`,onClick:()=>o(!t),children:[jsxRuntime.jsxs("div",{className:"liql-logMeta",children:[jsxRuntime.jsxs("span",{className:`liql-statusTag liql-tag-${s}`,children:[l(e.level)," ",e.level]}),jsxRuntime.jsx("span",{className:"liql-logTime",children:r(e.timestamp)})]}),jsxRuntime.jsxs("div",{className:"liql-logContent",children:[e.title&&jsxRuntime.jsxs("strong",{style:{color:"var(--liql-primary)"},children:[e.title,": "]}),e.message]}),t&&jsxRuntime.jsxs("div",{className:"liql-expandedContent",children:[e.stack&&jsxRuntime.jsxs("div",{style:{color:"var(--liql-error)",marginBottom:"8px"},children:[jsxRuntime.jsx("strong",{children:"Stack Trace:"}),jsxRuntime.jsx("pre",{style:{whiteSpace:"pre-wrap",fontSize:"10px",marginTop:"4px"},children:e.stack})]}),e.data&&jsxRuntime.jsxs("div",{children:[jsxRuntime.jsx("strong",{children:"Payload:"}),i(e.data)]}),!e.data&&!e.stack&&jsxRuntime.jsxs("div",{children:["Full message: ",e.message]})]})]})};var I=({logs:e,filter:t,search:o})=>{let r=j__default.default.useMemo(()=>e.filter(i=>{let l=t==="ALL"||i.level===t,s=o===""||i.message.toLowerCase().includes(o.toLowerCase())||(i.title||"").toLowerCase().includes(o.toLowerCase());return l&&s}).reverse(),[e,t,o]);return jsxRuntime.jsx("div",{className:"liql-logListContainer",children:jsxRuntime.jsx(reactVirtuoso.Virtuoso,{style:{height:"100%"},data:r,itemContent:(i,l)=>jsxRuntime.jsx(O,{log:l},l.id)})})};var P=()=>{let{state:e,setIsPanelOpen:t,dispatch:o}=m(),[r,i]=j.useState("ALL"),[l,s]=j.useState(""),c=()=>{confirm("Are you sure you want to clear all logs?")&&o({type:"CLEAR_LOGS"});},p=()=>{let a="data:text/json;charset=utf-8,"+encodeURIComponent(JSON.stringify(e.logs,null,2)),n=document.createElement("a");n.setAttribute("href",a),n.setAttribute("download",`logs_${new Date().getTime()}.json`),document.body.appendChild(n),n.click(),n.remove();};return e?jsxRuntime.jsxs("div",{className:"liql-panel",style:{right:"20px",bottom:"20px",width:"440px",height:"680px"},children:[jsxRuntime.jsxs("div",{className:"liql-panelHeader",children:[jsxRuntime.jsx("div",{className:"liql-titleGroup",children:jsxRuntime.jsxs("span",{className:"liql-title",children:["ReactLooger console [",e.logs.length,"]"]})}),jsxRuntime.jsxs("div",{className:"liql-controls",children:[jsxRuntime.jsx("button",{className:"liql-btnAction",onClick:p,title:"Export JSON",children:"\u{1F4E5}"}),jsxRuntime.jsx("button",{className:"liql-btnAction",onClick:c,title:"Clear Logs",children:"\u{1F5D1}\uFE0F"}),jsxRuntime.jsx("button",{className:"liql-btnAction",onClick:()=>t(false),title:"Minimize",children:"\u2796"})]})]}),jsxRuntime.jsxs("div",{className:"liql-filterBar",children:[jsxRuntime.jsx("input",{type:"text",placeholder:"Search entries...",className:"liql-searchInput",value:l,onChange:a=>s(a.target.value)}),jsxRuntime.jsxs("select",{className:"liql-selectFilter",value:r,onChange:a=>i(a.target.value),children:[jsxRuntime.jsx("option",{value:"ALL",children:"ALL"}),jsxRuntime.jsx("option",{value:"DEBUG",children:"DEBUG"}),jsxRuntime.jsx("option",{value:"ERROR",children:"ERROR"}),jsxRuntime.jsx("option",{value:"OBJECT",children:"OBJECTS"})]})]}),jsxRuntime.jsx(I,{logs:e.logs,filter:r,search:l}),jsxRuntime.jsxs("div",{className:"liql-footerActions",children:[jsxRuntime.jsx("div",{className:"liql-storageInfo",children:e.config.persistence?`DRIVER: ${e.config.persistenceDriver?.toUpperCase()}`:"SESSION MODE"}),jsxRuntime.jsx("button",{className:"liql-btnClose",onClick:()=>t(false),children:"CLOSE CONSOLE"})]})]}):null};var $e=()=>{let{isPanelOpen:e}=m();return jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(D,{}),e&&jsxRuntime.jsx(P,{})]})};exports.LoggerProvider=xe;exports.LoggerViewer=$e;exports.useLogger=Le;exports.useLoggerContext=m;//# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["#style-inject:#style-inject","../src/styles/logger.css","../src/storage/localStorageDriver.ts","../src/storage/indexedDBDriver.ts","../src/storage/index.ts","../src/context/LoggerContext.tsx","../src/hooks/useLogger.ts","../src/components/FloatingButton.tsx","../src/components/LogItem.tsx","../src/components/LogList.tsx","../src/components/LogPanel.tsx","../src/components/LoggerViewer.tsx"],"names":["styleInject","css","insertAt","head","style","LocalStorageDriver","key","logs","e","data","DB_NAME","STORE_NAME","DB_VERSION","IndexedDBDriver","openDB","db","tx","store","log","getStorageDriver","type","initialState","loggerReducer","state","action","newLogs","LoggerContext","createContext","LoggerProvider","children","config","dispatch","useReducer","isPanelOpen","setIsPanelOpen","React","storageRef","useRef","useEffect","loadedLogs","jsx","useLoggerContext","context","useContext","useLogger","addLog","useCallback","level","message","extra","debug","error","err","title","stack","object","clear","exportLogs","dataStr","downloadAnchorNode","FloatingButton","position","setPosition","useState","isDragging","dragOffset","handleMouseDown","handleTouchStart","touch","handleMove","clientX","clientY","onMouseMove","onTouchMove","onEnd","handleClick","jsxs","LogItem","isExpanded","setIsExpanded","formatTime","isoString","renderJson","getIcon","levelClass","LogList","filter","search","filteredLogs","matchesFilter","matchesSearch","Virtuoso","_index","LogPanel","setFilter","setSearch","clearLogs","LoggerViewer","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["\n export default function styleInject(css, { insertAt } = {}) {\n if (!css || typeof document === 'undefined') return\n \n const head = document.head || document.getElementsByTagName('head')[0]\n const style = document.createElement('style')\n style.type = 'text/css'\n \n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild)\n } else {\n head.appendChild(style)\n }\n } else {\n head.appendChild(style)\n }\n \n if (style.styleSheet) {\n style.styleSheet.cssText = css\n } else {\n style.appendChild(document.createTextNode(css))\n }\n }\n ","import styleInject from '#style-inject';styleInject(\":root{--liql-primary: #3498db;--liql-primary-glow: rgba(52, 152, 219, .5);--liql-error: #ff4d4d;--liql-error-glow: rgba(255, 77, 77, .4);--liql-object: #00ff88;--liql-object-glow: rgba(0, 255, 136, .4);--liql-glass-bg: rgba(15, 23, 42, .7);--liql-glass-border: rgba(255, 255, 255, .1);--liql-glass-edge: rgba(255, 255, 255, .05);--liql-text-main: #f8fafc;--liql-text-muted: #94a3b8;--liql-font-family: \\\"Inter\\\", -apple-system, BlinkMacSystemFont, \\\"Segoe UI\\\", Roboto, sans-serif}.liql-floatingButton{position:fixed;width:60px;height:60px;border-radius:50%;background:radial-gradient(circle at 30% 30%,rgba(255,255,255,.15),transparent),linear-gradient(135deg,var(--liql-primary),#2980b9);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);box-shadow:0 8px 32px #0006,0 0 15px var(--liql-primary-glow);display:flex;align-items:center;justify-content:center;cursor:grab;z-index:10000;border:1px solid var(--liql-glass-border);transition:all .4s cubic-bezier(.175,.885,.32,1.275);font-size:26px;color:#fff;user-select:none;touch-action:none}.liql-floatingButton:hover{transform:scale(1.1) rotate(5deg);box-shadow:0 12px 40px #00000080,0 0 25px var(--liql-primary-glow)}.liql-badge{position:absolute;top:-2px;right:-2px;background:var(--liql-error);color:#fff;border-radius:12px;min-width:20px;height:20px;padding:0 6px;font-size:11px;font-weight:700;display:flex;align-items:center;justify-content:center;border:2px solid #0f172a;box-shadow:0 0 10px var(--liql-error-glow)}.liql-panel{position:fixed;background:var(--liql-glass-bg);backdrop-filter:blur(25px) saturate(180%);-webkit-backdrop-filter:blur(25px) saturate(180%);box-shadow:0 20px 50px #0009,inset 0 0 0 1px var(--liql-glass-edge);border:1px solid var(--liql-glass-border);border-radius:20px;display:flex;flex-direction:column;z-index:10001;overflow:hidden;font-family:var(--liql-font-family);color:var(--liql-text-main);animation:liql-slideUp .4s cubic-bezier(.23,1,.32,1)}.liql-panelHeader{background:#ffffff0d;padding:16px 20px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--liql-glass-border)}.liql-title{font-weight:700;letter-spacing:.5px;text-transform:uppercase;font-size:12px;color:var(--liql-text-muted)}.liql-controls{display:flex;gap:12px}.liql-btnAction{background:#ffffff14;border:1px solid var(--liql-glass-border);color:var(--liql-text-main);width:32px;height:32px;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}.liql-btnAction:hover{background:#ffffff26;transform:translateY(-2px)}.liql-filterBar{padding:12px 20px;display:flex;gap:10px;background:#0003;align-items:center}.liql-searchInput{flex:1;background:#ffffff0d;border:1px solid var(--liql-glass-border);border-radius:10px;padding:8px 14px;color:#fff;font-size:13px}.liql-searchInput:focus{outline:none;border-color:var(--liql-primary);background:#ffffff14}.liql-selectFilter{background:#ffffff0d;border:1px solid var(--liql-glass-border);border-radius:10px;padding:8px;color:#fff;font-size:12px;cursor:pointer}.liql-logListContainer{flex:1;padding:10px}.liql-logItem{margin-bottom:10px;padding:16px;border-radius:14px;background:#ffffff08;border:1px solid var(--liql-glass-border);transition:all .3s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden}.liql-logItem:hover{background:#ffffff0f;border-color:#fff3;transform:scale(1.01)}.liql-logItem-debug{border-left:4px solid var(--liql-primary)}.liql-logItem-error{border-left:4px solid var(--liql-error);box-shadow:inset 0 0 15px var(--liql-error-glow)}.liql-logItem-object{border-left:4px solid var(--liql-object);box-shadow:inset 0 0 15px var(--liql-object-glow)}.liql-logMeta{display:flex;justify-content:space-between;margin-bottom:8px;font-size:11px;font-weight:500;text-transform:uppercase}.liql-statusTag{padding:2px 8px;border-radius:6px;font-size:9px;font-weight:700}.liql-tag-debug{background:#3498db33;color:#3498db}.liql-tag-error{background:#ff4d4d33;color:#ff4d4d}.liql-tag-object{background:#0f83;color:#0f8}.liql-logTime{color:var(--liql-text-muted)}.liql-logContent{font-size:14px;line-height:1.5;color:#e2e8f0}.liql-expandedContent{margin-top:12px;padding:12px;background:#0000004d;border-radius:10px;font-family:JetBrains Mono,monospace;font-size:12px;border:1px solid var(--liql-glass-border);color:#a5f3fc;overflow-x:auto}.liql-footerActions{padding:14px 20px;background:#0003;display:flex;justify-content:space-between;align-items:center;border-top:1px solid var(--liql-glass-border)}.liql-storageInfo{font-size:10px;color:var(--liql-text-muted)}.liql-btnClose{background:linear-gradient(135deg,var(--liql-primary),#2980b9);border:none;padding:8px 16px;border-radius:8px;color:#fff;font-weight:600;font-size:13px;cursor:pointer;box-shadow:0 4px 12px var(--liql-primary-glow)}@keyframes liql-slideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}@media(max-width:600px){.liql-panel{width:94%!important;height:85%!important;bottom:3%!important;right:3%!important;left:3%!important;border-radius:24px}}\\n\")","import { LogEntry, StorageDriver } from '../types';\n\nexport class LocalStorageDriver implements StorageDriver {\n private key: string;\n\n constructor(key: string = 'ionic_react_logger_logs') {\n this.key = key;\n }\n\n async save(logs: LogEntry[]): Promise<void> {\n try {\n localStorage.setItem(this.key, JSON.stringify(logs));\n } catch (e) {\n console.error('Failed to save logs to localStorage', e);\n }\n }\n\n async load(): Promise<LogEntry[]> {\n try {\n const data = localStorage.getItem(this.key);\n return data ? JSON.parse(data) : [];\n } catch (e) {\n console.error('Failed to load logs from localStorage', e);\n return [];\n }\n }\n\n async clear(): Promise<void> {\n localStorage.removeItem(this.key);\n }\n}\n","import { openDB, IDBPDatabase } from 'idb';\nimport { LogEntry, StorageDriver } from '../types';\n\nconst DB_NAME = 'IonicReactLoggerDB';\nconst STORE_NAME = 'logs';\nconst DB_VERSION = 1;\n\nexport class IndexedDBDriver implements StorageDriver {\n private dbPromise: Promise<IDBPDatabase>;\n\n constructor() {\n this.dbPromise = openDB(DB_NAME, DB_VERSION, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME, { keyPath: 'id' });\n }\n },\n });\n }\n\n async save(logs: LogEntry[]): Promise<void> {\n const db = await this.dbPromise;\n const tx = db.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n\n // Clear existing logs first or manage them as needed\n // Simple implementation: clear and re-save everything to match the expected behavior of StorageDriver\n await store.clear();\n for (const log of logs) {\n await store.put(log);\n }\n await tx.done;\n }\n\n async load(): Promise<LogEntry[]> {\n const db = await this.dbPromise;\n return db.getAll(STORE_NAME);\n }\n\n async clear(): Promise<void> {\n const db = await this.dbPromise;\n const tx = db.transaction(STORE_NAME, 'readwrite');\n await tx.objectStore(STORE_NAME).clear();\n await tx.done;\n }\n}\n","import { StorageDriver, LogEntry } from '../types';\nimport { LocalStorageDriver } from './localStorageDriver';\nimport { IndexedDBDriver } from './indexedDBDriver';\n\nexport function getStorageDriver(type: 'localStorage' | 'indexedDB'): StorageDriver {\n if (type === 'indexedDB') {\n return new IndexedDBDriver();\n }\n return new LocalStorageDriver();\n}\n\nexport * from './localStorageDriver';\nexport * from './indexedDBDriver';\n","import React, { createContext, useContext, useReducer, useEffect, useCallback, useRef } from 'react';\nimport { LogEntry, LogLevel, LoggerConfig, StorageDriver } from '../types';\nimport { getStorageDriver } from '../storage';\n\ninterface LoggerState {\n logs: LogEntry[];\n config: LoggerConfig;\n unreadCount: number;\n}\n\ntype LoggerAction =\n | { type: 'ADD_LOG'; log: LogEntry }\n | { type: 'SET_LOGS'; logs: LogEntry[] }\n | { type: 'CLEAR_LOGS' }\n | { type: 'SET_CONFIG'; config: Partial<LoggerConfig> }\n | { type: 'RESET_UNREAD' }\n | { type: 'INCREMENT_UNREAD' };\n\nconst initialState: LoggerState = {\n logs: [],\n config: {\n persistence: false,\n persistenceDriver: 'localStorage',\n maxLogs: 500,\n },\n unreadCount: 0,\n};\n\nfunction loggerReducer(state: LoggerState, action: LoggerAction): LoggerState {\n switch (action.type) {\n case 'ADD_LOG': {\n const newLogs = [action.log, ...state.logs].slice(0, state.config.maxLogs);\n return {\n ...state,\n logs: newLogs,\n };\n }\n case 'SET_LOGS':\n return { ...state, logs: action.logs };\n case 'CLEAR_LOGS':\n return { ...state, logs: [], unreadCount: 0 };\n case 'SET_CONFIG':\n return { ...state, config: { ...state.config, ...action.config } };\n case 'RESET_UNREAD':\n return { ...state, unreadCount: 0 };\n case 'INCREMENT_UNREAD':\n return { ...state, unreadCount: state.unreadCount + 1 };\n default:\n return state;\n }\n}\n\ninterface LoggerContextType {\n state: LoggerState;\n dispatch: React.Dispatch<LoggerAction>;\n isPanelOpen: boolean;\n setIsPanelOpen: (open: boolean) => void;\n}\n\nconst LoggerContext = createContext<LoggerContextType | undefined>(undefined);\n\n/**\n * Provides the logging context to all child components.\n * This should wrap your entire application (e.g. in App.tsx).\n * \n * @param config Optional initial configuration for persistence and limits.\n */\nexport const LoggerProvider: React.FC<{ children: React.ReactNode; config?: LoggerConfig }> = ({\n children,\n config,\n}) => {\n const [state, dispatch] = useReducer(loggerReducer, {\n ...initialState,\n config: { ...initialState.config, ...config },\n });\n const [isPanelOpen, setIsPanelOpen] = React.useState(false);\n const storageRef = useRef<StorageDriver | null>(null);\n\n // Initialize storage driver\n useEffect(() => {\n if (state.config.persistence) {\n storageRef.current = getStorageDriver(state.config.persistenceDriver || 'localStorage');\n storageRef.current.load().then((loadedLogs) => {\n dispatch({ type: 'SET_LOGS', logs: loadedLogs });\n });\n }\n }, [state.config.persistence, state.config.persistenceDriver]);\n\n // Persist logs when they change\n useEffect(() => {\n if (state.config.persistence && storageRef.current) {\n storageRef.current.save(state.logs);\n }\n }, [state.logs, state.config.persistence]);\n\n return (\n <LoggerContext.Provider value={{ state, dispatch, isPanelOpen, setIsPanelOpen }}>\n {children}\n </LoggerContext.Provider>\n );\n};\n\n/**\n * Internal hook to access the logger context state.\n * Use the public `useLogger` hook for standard logging operations.\n */\nexport const useLoggerContext = () => {\n const context = useContext(LoggerContext);\n if (!context) {\n throw new Error('useLoggerContext must be used within a LoggerProvider');\n }\n return context;\n};\n","import { useCallback } from 'react';\nimport { useLoggerContext } from '../context/LoggerContext';\nimport { LogEntry, LogLevel } from '../types';\n\n/**\n * Main hook to interact with the Liquid Glass Logger.\n * \n * Provides methods for different logging levels and allows accessing\n * the current state of logs.\n * \n * @example\n * ```tsx\n * const { debug, error, object } = useLogger();\n * \n * debug('App started');\n * error(new Error('Failed to fetch'), 'API Error');\n * object({ user: 'John' }, 'Current User');\n * ```\n */\nexport const useLogger = () => {\n const { state, dispatch, isPanelOpen } = useLoggerContext();\n\n const addLog = useCallback((level: LogLevel, message: string, extra?: { title?: string, data?: any, stack?: string }) => {\n const log: LogEntry = {\n id: Math.random().toString(36).substring(2, 9),\n level,\n message,\n timestamp: new Date().toISOString(),\n ...extra\n };\n\n dispatch({ type: 'ADD_LOG', log: log });\n state.config.onLogAdded?.(log);\n }, [dispatch, state.config]);\n\n /**\n * Log a debug message (Blue neon)\n */\n const debug = useCallback((message: string) => addLog('DEBUG', message), [addLog]);\n\n /**\n * Log an error. If an Error object is passed, it automatically captures the stack trace.\n * (Red neon)\n */\n const error = useCallback((err: string | Error, title?: string) => {\n const message = err instanceof Error ? err.message : err;\n const stack = err instanceof Error ? err.stack : undefined;\n addLog('ERROR', message, { title: title || 'Error', stack });\n }, [addLog]);\n\n /**\n * Log a data object for inspection.\n * (Green neon)\n */\n const object = useCallback((data: any, title?: string) => {\n addLog('OBJECT', 'Object visualization', { title: title || 'Data Object', data });\n }, [addLog]);\n\n /**\n * Clears all logs from the current session and persistent storage.\n */\n const clear = useCallback(() => {\n dispatch({ type: 'CLEAR_LOGS' });\n }, [dispatch]);\n\n /**\n * Exports all logs as a JSON file download.\n */\n const exportLogs = useCallback(() => {\n const dataStr = \"data:text/json;charset=utf-8,\" + encodeURIComponent(JSON.stringify(state.logs, null, 2));\n const downloadAnchorNode = document.createElement('a');\n downloadAnchorNode.setAttribute(\"href\", dataStr);\n downloadAnchorNode.setAttribute(\"download\", `logs_${new Date().getTime()}.json`);\n document.body.appendChild(downloadAnchorNode);\n downloadAnchorNode.click();\n downloadAnchorNode.remove();\n }, [state.logs]);\n\n return {\n /** Register a debug message */\n debug,\n /** Register an error or exception */\n error,\n /** Register an object for JSON visualization */\n object,\n /** Clear all history */\n clear,\n /** Download history as JSON */\n exportLogs,\n /** List of all captured logs */\n logs: state.logs,\n /** Count of logs not yet viewed */\n unreadCount: state.unreadCount,\n };\n};\n","import React, { useState, useRef, useEffect, useCallback } from 'react';\nimport { useLoggerContext } from '../context/LoggerContext';\n\n/**\n * Draggable floating trigger button (Bug 🐞).\n * Displays unread counts and opens the main logging console.\n */\nexport const FloatingButton: React.FC = () => {\n const { state, setIsPanelOpen } = useLoggerContext();\n const [position, setPosition] = useState({ x: window.innerWidth - 80, y: window.innerHeight - 80 });\n const isDragging = useRef(false);\n const dragOffset = useRef({ x: 0, y: 0 });\n\n const handleMouseDown = (e: React.MouseEvent) => {\n isDragging.current = true;\n dragOffset.current = {\n x: e.clientX - position.x,\n y: e.clientY - position.y\n };\n };\n\n const handleTouchStart = (e: React.TouchEvent) => {\n isDragging.current = true;\n const touch = e.touches[0];\n dragOffset.current = {\n x: touch.clientX - position.x,\n y: touch.clientY - position.y\n };\n };\n\n const handleMove = useCallback((clientX: number, clientY: number) => {\n if (!isDragging.current) return;\n\n setPosition({\n x: Math.max(0, Math.min(window.innerWidth - 60, clientX - dragOffset.current.x)),\n y: Math.max(0, Math.min(window.innerHeight - 60, clientY - dragOffset.current.y))\n });\n }, []);\n\n useEffect(() => {\n const onMouseMove = (e: MouseEvent) => handleMove(e.clientX, e.clientY);\n const onTouchMove = (e: TouchEvent) => handleMove(e.touches[0].clientX, e.touches[0].clientY);\n const onEnd = () => (isDragging.current = false);\n\n window.addEventListener('mousemove', onMouseMove);\n window.addEventListener('touchmove', onTouchMove);\n window.addEventListener('mouseup', onEnd);\n window.addEventListener('touchend', onEnd);\n\n return () => {\n window.removeEventListener('mousemove', onMouseMove);\n window.removeEventListener('touchmove', onTouchMove);\n window.removeEventListener('mouseup', onEnd);\n window.removeEventListener('touchend', onEnd);\n };\n }, [handleMove]);\n\n const handleClick = () => {\n if (isDragging.current) return;\n setIsPanelOpen(true);\n };\n\n return (\n <div\n className=\"liql-floatingButton\"\n style={{ left: `${position.x}px`, top: `${position.y}px` }}\n onMouseDown={handleMouseDown}\n onTouchStart={handleTouchStart}\n onClick={handleClick}\n aria-label=\"Open Logger\"\n >\n 🐞\n {state.unreadCount > 0 && (\n <div className=\"liql-badge\">{state.unreadCount}</div>\n )}\n </div>\n );\n};\n","import React, { useState } from 'react';\nimport { LogEntry } from '../types';\n\ninterface LogItemProps {\n log: LogEntry;\n}\n\nexport const LogItem: React.FC<LogItemProps> = ({ log }) => {\n const [isExpanded, setIsExpanded] = useState(false);\n\n const formatTime = (isoString: string) => {\n const date = new Date(isoString);\n return date.toTimeString().split(' ')[0];\n };\n\n const renderJson = (data: any) => {\n try {\n return (\n <pre className=\"liql-expandedContent\">\n {JSON.stringify(data, null, 2)}\n </pre>\n );\n } catch (e) {\n return <div>Error parsing object</div>;\n }\n };\n\n const getIcon = (level: string) => {\n switch (level) {\n case 'DEBUG': return '🐞';\n case 'ERROR': return '❌';\n case 'OBJECT': return '📦';\n default: return '📝';\n }\n };\n\n const levelClass = log.level.toLowerCase();\n\n return (\n <div\n className={`liql-logItem liql-logItem-${levelClass}`}\n onClick={() => setIsExpanded(!isExpanded)}\n >\n <div className=\"liql-logMeta\">\n <span className={`liql-statusTag liql-tag-${levelClass}`}>\n {getIcon(log.level)} {log.level}\n </span>\n <span className=\"liql-logTime\">{formatTime(log.timestamp)}</span>\n </div>\n <div className=\"liql-logContent\">\n {log.title && <strong style={{ color: 'var(--liql-primary)' }}>{log.title}: </strong>}\n {log.message}\n </div>\n\n {isExpanded && (\n <div className=\"liql-expandedContent\">\n {log.stack && (\n <div style={{ color: 'var(--liql-error)', marginBottom: '8px' }}>\n <strong>Stack Trace:</strong>\n <pre style={{ whiteSpace: 'pre-wrap', fontSize: '10px', marginTop: '4px' }}>{log.stack}</pre>\n </div>\n )}\n {log.data && (\n <div>\n <strong>Payload:</strong>\n {renderJson(log.data)}\n </div>\n )}\n {!log.data && !log.stack && <div>Full message: {log.message}</div>}\n </div>\n )}\n </div>\n );\n};\n","import React from 'react';\nimport { Virtuoso } from 'react-virtuoso';\nimport { LogItem } from './LogItem';\nimport { LogEntry } from '../types';\n\ninterface LogListProps {\n logs: LogEntry[];\n filter: string;\n search: string;\n}\n\nexport const LogList: React.FC<LogListProps> = ({ logs, filter, search }) => {\n const filteredLogs = React.useMemo(() => {\n return logs.filter(log => {\n const matchesFilter = filter === 'ALL' || log.level === filter;\n const matchesSearch = search === '' ||\n log.message.toLowerCase().includes(search.toLowerCase()) ||\n (log.title || '').toLowerCase().includes(search.toLowerCase());\n return matchesFilter && matchesSearch;\n }).reverse();\n }, [logs, filter, search]);\n\n return (\n <div className=\"liql-logListContainer\">\n <Virtuoso\n style={{ height: '100%' }}\n data={filteredLogs}\n itemContent={(_index: number, log: LogEntry) => <LogItem key={log.id} log={log} />}\n />\n </div>\n );\n};\n","import React, { useState } from 'react';\nimport { useLoggerContext } from '../context/LoggerContext';\nimport { LogList } from './LogList';\n\n/**\n * Main 'Liquid Glass' logging console.\n * Contains the log list, filtering, search, and action controls.\n */\nexport const LogPanel: React.FC = () => {\n const { state, setIsPanelOpen, dispatch } = useLoggerContext();\n const [filter, setFilter] = useState('ALL');\n const [search, setSearch] = useState('');\n\n const clearLogs = () => {\n if (confirm('Are you sure you want to clear all logs?')) {\n dispatch({ type: 'CLEAR_LOGS' });\n }\n };\n\n const exportLogs = () => {\n const dataStr = \"data:text/json;charset=utf-8,\" + encodeURIComponent(JSON.stringify(state.logs, null, 2));\n const downloadAnchorNode = document.createElement('a');\n downloadAnchorNode.setAttribute(\"href\", dataStr);\n downloadAnchorNode.setAttribute(\"download\", `logs_${new Date().getTime()}.json`);\n document.body.appendChild(downloadAnchorNode);\n downloadAnchorNode.click();\n downloadAnchorNode.remove();\n };\n\n if (!state) return null;\n\n return (\n <div className=\"liql-panel\" style={{ right: '20px', bottom: '20px', width: '440px', height: '680px' }}>\n <div className=\"liql-panelHeader\">\n <div className=\"liql-titleGroup\">\n <span className=\"liql-title\">ReactLooger console [{state.logs.length}]</span>\n </div>\n <div className=\"liql-controls\">\n <button className=\"liql-btnAction\" onClick={exportLogs} title=\"Export JSON\">📥</button>\n <button className=\"liql-btnAction\" onClick={clearLogs} title=\"Clear Logs\">🗑️</button>\n <button className=\"liql-btnAction\" onClick={() => setIsPanelOpen(false)} title=\"Minimize\">➖</button>\n </div>\n </div>\n\n <div className=\"liql-filterBar\">\n <input\n type=\"text\"\n placeholder=\"Search entries...\"\n className=\"liql-searchInput\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n <select\n className=\"liql-selectFilter\"\n value={filter}\n onChange={(e) => setFilter(e.target.value)}\n >\n <option value=\"ALL\">ALL</option>\n <option value=\"DEBUG\">DEBUG</option>\n <option value=\"ERROR\">ERROR</option>\n <option value=\"OBJECT\">OBJECTS</option>\n </select>\n </div>\n\n <LogList logs={state.logs} filter={filter} search={search} />\n\n <div className=\"liql-footerActions\">\n <div className=\"liql-storageInfo\">\n {state.config.persistence ? `DRIVER: ${state.config.persistenceDriver?.toUpperCase()}` : 'SESSION MODE'}\n </div>\n <button className=\"liql-btnClose\" onClick={() => setIsPanelOpen(false)}>CLOSE CONSOLE</button>\n </div>\n </div>\n );\n};\n","import React from 'react';\nimport { useLoggerContext } from '../context/LoggerContext';\nimport { FloatingButton } from './FloatingButton';\nimport { LogPanel } from './LogPanel';\n\n/**\n * Main entry point for the Logger UI.\n * Renders the floating trigger button and conditionally shows the glass panel.\n * Should be placed at the root level of your application.\n */\nexport const LoggerViewer: React.FC = () => {\n const { isPanelOpen } = useLoggerContext();\n\n return (\n <>\n <FloatingButton />\n {isPanelOpen && <LogPanel />}\n </>\n );\n};\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,3 @@
1
+ import j,{createContext,useReducer,useRef,useEffect,useContext,useCallback,useState}from'react';import {openDB}from'idb';import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {Virtuoso}from'react-virtuoso';function w(e,{insertAt:t}={}){if(typeof document>"u")return;let o=document.head||document.getElementsByTagName("head")[0],r=document.createElement("style");r.type="text/css",t==="top"&&o.firstChild?o.insertBefore(r,o.firstChild):o.appendChild(r),r.styleSheet?r.styleSheet.cssText=e:r.appendChild(document.createTextNode(e));}w(`:root{--liql-primary: #3498db;--liql-primary-glow: rgba(52, 152, 219, .5);--liql-error: #ff4d4d;--liql-error-glow: rgba(255, 77, 77, .4);--liql-object: #00ff88;--liql-object-glow: rgba(0, 255, 136, .4);--liql-glass-bg: rgba(15, 23, 42, .7);--liql-glass-border: rgba(255, 255, 255, .1);--liql-glass-edge: rgba(255, 255, 255, .05);--liql-text-main: #f8fafc;--liql-text-muted: #94a3b8;--liql-font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif}.liql-floatingButton{position:fixed;width:60px;height:60px;border-radius:50%;background:radial-gradient(circle at 30% 30%,rgba(255,255,255,.15),transparent),linear-gradient(135deg,var(--liql-primary),#2980b9);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);box-shadow:0 8px 32px #0006,0 0 15px var(--liql-primary-glow);display:flex;align-items:center;justify-content:center;cursor:grab;z-index:10000;border:1px solid var(--liql-glass-border);transition:all .4s cubic-bezier(.175,.885,.32,1.275);font-size:26px;color:#fff;user-select:none;touch-action:none}.liql-floatingButton:hover{transform:scale(1.1) rotate(5deg);box-shadow:0 12px 40px #00000080,0 0 25px var(--liql-primary-glow)}.liql-badge{position:absolute;top:-2px;right:-2px;background:var(--liql-error);color:#fff;border-radius:12px;min-width:20px;height:20px;padding:0 6px;font-size:11px;font-weight:700;display:flex;align-items:center;justify-content:center;border:2px solid #0f172a;box-shadow:0 0 10px var(--liql-error-glow)}.liql-panel{position:fixed;background:var(--liql-glass-bg);backdrop-filter:blur(25px) saturate(180%);-webkit-backdrop-filter:blur(25px) saturate(180%);box-shadow:0 20px 50px #0009,inset 0 0 0 1px var(--liql-glass-edge);border:1px solid var(--liql-glass-border);border-radius:20px;display:flex;flex-direction:column;z-index:10001;overflow:hidden;font-family:var(--liql-font-family);color:var(--liql-text-main);animation:liql-slideUp .4s cubic-bezier(.23,1,.32,1)}.liql-panelHeader{background:#ffffff0d;padding:16px 20px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--liql-glass-border)}.liql-title{font-weight:700;letter-spacing:.5px;text-transform:uppercase;font-size:12px;color:var(--liql-text-muted)}.liql-controls{display:flex;gap:12px}.liql-btnAction{background:#ffffff14;border:1px solid var(--liql-glass-border);color:var(--liql-text-main);width:32px;height:32px;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}.liql-btnAction:hover{background:#ffffff26;transform:translateY(-2px)}.liql-filterBar{padding:12px 20px;display:flex;gap:10px;background:#0003;align-items:center}.liql-searchInput{flex:1;background:#ffffff0d;border:1px solid var(--liql-glass-border);border-radius:10px;padding:8px 14px;color:#fff;font-size:13px}.liql-searchInput:focus{outline:none;border-color:var(--liql-primary);background:#ffffff14}.liql-selectFilter{background:#ffffff0d;border:1px solid var(--liql-glass-border);border-radius:10px;padding:8px;color:#fff;font-size:12px;cursor:pointer}.liql-logListContainer{flex:1;padding:10px}.liql-logItem{margin-bottom:10px;padding:16px;border-radius:14px;background:#ffffff08;border:1px solid var(--liql-glass-border);transition:all .3s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden}.liql-logItem:hover{background:#ffffff0f;border-color:#fff3;transform:scale(1.01)}.liql-logItem-debug{border-left:4px solid var(--liql-primary)}.liql-logItem-error{border-left:4px solid var(--liql-error);box-shadow:inset 0 0 15px var(--liql-error-glow)}.liql-logItem-object{border-left:4px solid var(--liql-object);box-shadow:inset 0 0 15px var(--liql-object-glow)}.liql-logMeta{display:flex;justify-content:space-between;margin-bottom:8px;font-size:11px;font-weight:500;text-transform:uppercase}.liql-statusTag{padding:2px 8px;border-radius:6px;font-size:9px;font-weight:700}.liql-tag-debug{background:#3498db33;color:#3498db}.liql-tag-error{background:#ff4d4d33;color:#ff4d4d}.liql-tag-object{background:#0f83;color:#0f8}.liql-logTime{color:var(--liql-text-muted)}.liql-logContent{font-size:14px;line-height:1.5;color:#e2e8f0}.liql-expandedContent{margin-top:12px;padding:12px;background:#0000004d;border-radius:10px;font-family:JetBrains Mono,monospace;font-size:12px;border:1px solid var(--liql-glass-border);color:#a5f3fc;overflow-x:auto}.liql-footerActions{padding:14px 20px;background:#0003;display:flex;justify-content:space-between;align-items:center;border-top:1px solid var(--liql-glass-border)}.liql-storageInfo{font-size:10px;color:var(--liql-text-muted)}.liql-btnClose{background:linear-gradient(135deg,var(--liql-primary),#2980b9);border:none;padding:8px 16px;border-radius:8px;color:#fff;font-weight:600;font-size:13px;cursor:pointer;box-shadow:0 4px 12px var(--liql-primary-glow)}@keyframes liql-slideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}@media(max-width:600px){.liql-panel{width:94%!important;height:85%!important;bottom:3%!important;right:3%!important;left:3%!important;border-radius:24px}}
2
+ `);var L=class{key;constructor(t="ionic_react_logger_logs"){this.key=t;}async save(t){try{localStorage.setItem(this.key,JSON.stringify(t));}catch(o){console.error("Failed to save logs to localStorage",o);}}async load(){try{let t=localStorage.getItem(this.key);return t?JSON.parse(t):[]}catch(t){return console.error("Failed to load logs from localStorage",t),[]}}async clear(){localStorage.removeItem(this.key);}};var _="IonicReactLoggerDB",x="logs",M=1,q=class{dbPromise;constructor(){this.dbPromise=openDB(_,M,{upgrade(t){t.objectStoreNames.contains(x)||t.createObjectStore(x,{keyPath:"id"});}});}async save(t){let r=(await this.dbPromise).transaction(x,"readwrite"),i=r.objectStore(x);await i.clear();for(let l of t)await i.put(l);await r.done;}async load(){return (await this.dbPromise).getAll(x)}async clear(){let o=(await this.dbPromise).transaction(x,"readwrite");await o.objectStore(x).clear(),await o.done;}};function C(e){return e==="indexedDB"?new q:new L}var R={logs:[],config:{persistence:false,persistenceDriver:"localStorage",maxLogs:500},unreadCount:0};function J(e,t){switch(t.type){case "ADD_LOG":{let o=[t.log,...e.logs].slice(0,e.config.maxLogs);return {...e,logs:o}}case "SET_LOGS":return {...e,logs:t.logs};case "CLEAR_LOGS":return {...e,logs:[],unreadCount:0};case "SET_CONFIG":return {...e,config:{...e.config,...t.config}};case "RESET_UNREAD":return {...e,unreadCount:0};case "INCREMENT_UNREAD":return {...e,unreadCount:e.unreadCount+1};default:return e}}var k=createContext(void 0),xe=({children:e,config:t})=>{let[o,r]=useReducer(J,{...R,config:{...R.config,...t}}),[i,l]=j.useState(false),s=useRef(null);return useEffect(()=>{o.config.persistence&&(s.current=C(o.config.persistenceDriver||"localStorage"),s.current.load().then(c=>{r({type:"SET_LOGS",logs:c});}));},[o.config.persistence,o.config.persistenceDriver]),useEffect(()=>{o.config.persistence&&s.current&&s.current.save(o.logs);},[o.logs,o.config.persistence]),jsx(k.Provider,{value:{state:o,dispatch:r,isPanelOpen:i,setIsPanelOpen:l},children:e})},m=()=>{let e=useContext(k);if(!e)throw new Error("useLoggerContext must be used within a LoggerProvider");return e};var Le=()=>{let{state:e,dispatch:t,isPanelOpen:o}=m(),r=useCallback((a,n,g)=>{let f={id:Math.random().toString(36).substring(2,9),level:a,message:n,timestamp:new Date().toISOString(),...g};t({type:"ADD_LOG",log:f}),e.config.onLogAdded?.(f);},[t,e.config]),i=useCallback(a=>r("DEBUG",a),[r]),l=useCallback((a,n)=>{let g=a instanceof Error?a.message:a,f=a instanceof Error?a.stack:void 0;r("ERROR",g,{title:n||"Error",stack:f});},[r]),s=useCallback((a,n)=>{r("OBJECT","Object visualization",{title:n||"Data Object",data:a});},[r]),c=useCallback(()=>{t({type:"CLEAR_LOGS"});},[t]),p=useCallback(()=>{let a="data:text/json;charset=utf-8,"+encodeURIComponent(JSON.stringify(e.logs,null,2)),n=document.createElement("a");n.setAttribute("href",a),n.setAttribute("download",`logs_${new Date().getTime()}.json`),document.body.appendChild(n),n.click(),n.remove();},[e.logs]);return {debug:i,error:l,object:s,clear:c,exportLogs:p,logs:e.logs,unreadCount:e.unreadCount}};var D=()=>{let{state:e,setIsPanelOpen:t}=m(),[o,r]=useState({x:window.innerWidth-80,y:window.innerHeight-80}),i=useRef(false),l=useRef({x:0,y:0}),s=n=>{i.current=true,l.current={x:n.clientX-o.x,y:n.clientY-o.y};},c=n=>{i.current=true;let g=n.touches[0];l.current={x:g.clientX-o.x,y:g.clientY-o.y};},p=useCallback((n,g)=>{i.current&&r({x:Math.max(0,Math.min(window.innerWidth-60,n-l.current.x)),y:Math.max(0,Math.min(window.innerHeight-60,g-l.current.y))});},[]);useEffect(()=>{let n=y=>p(y.clientX,y.clientY),g=y=>p(y.touches[0].clientX,y.touches[0].clientY),f=()=>i.current=false;return window.addEventListener("mousemove",n),window.addEventListener("touchmove",g),window.addEventListener("mouseup",f),window.addEventListener("touchend",f),()=>{window.removeEventListener("mousemove",n),window.removeEventListener("touchmove",g),window.removeEventListener("mouseup",f),window.removeEventListener("touchend",f);}},[p]);let a=()=>{i.current||t(true);};return jsxs("div",{className:"liql-floatingButton",style:{left:`${o.x}px`,top:`${o.y}px`},onMouseDown:s,onTouchStart:c,onClick:a,"aria-label":"Open Logger",children:["\u{1F41E}",e.unreadCount>0&&jsx("div",{className:"liql-badge",children:e.unreadCount})]})};var O=({log:e})=>{let[t,o]=useState(false),r=c=>new Date(c).toTimeString().split(" ")[0],i=c=>{try{return jsx("pre",{className:"liql-expandedContent",children:JSON.stringify(c,null,2)})}catch{return jsx("div",{children:"Error parsing object"})}},l=c=>{switch(c){case "DEBUG":return "\u{1F41E}";case "ERROR":return "\u274C";case "OBJECT":return "\u{1F4E6}";default:return "\u{1F4DD}"}},s=e.level.toLowerCase();return jsxs("div",{className:`liql-logItem liql-logItem-${s}`,onClick:()=>o(!t),children:[jsxs("div",{className:"liql-logMeta",children:[jsxs("span",{className:`liql-statusTag liql-tag-${s}`,children:[l(e.level)," ",e.level]}),jsx("span",{className:"liql-logTime",children:r(e.timestamp)})]}),jsxs("div",{className:"liql-logContent",children:[e.title&&jsxs("strong",{style:{color:"var(--liql-primary)"},children:[e.title,": "]}),e.message]}),t&&jsxs("div",{className:"liql-expandedContent",children:[e.stack&&jsxs("div",{style:{color:"var(--liql-error)",marginBottom:"8px"},children:[jsx("strong",{children:"Stack Trace:"}),jsx("pre",{style:{whiteSpace:"pre-wrap",fontSize:"10px",marginTop:"4px"},children:e.stack})]}),e.data&&jsxs("div",{children:[jsx("strong",{children:"Payload:"}),i(e.data)]}),!e.data&&!e.stack&&jsxs("div",{children:["Full message: ",e.message]})]})]})};var I=({logs:e,filter:t,search:o})=>{let r=j.useMemo(()=>e.filter(i=>{let l=t==="ALL"||i.level===t,s=o===""||i.message.toLowerCase().includes(o.toLowerCase())||(i.title||"").toLowerCase().includes(o.toLowerCase());return l&&s}).reverse(),[e,t,o]);return jsx("div",{className:"liql-logListContainer",children:jsx(Virtuoso,{style:{height:"100%"},data:r,itemContent:(i,l)=>jsx(O,{log:l},l.id)})})};var P=()=>{let{state:e,setIsPanelOpen:t,dispatch:o}=m(),[r,i]=useState("ALL"),[l,s]=useState(""),c=()=>{confirm("Are you sure you want to clear all logs?")&&o({type:"CLEAR_LOGS"});},p=()=>{let a="data:text/json;charset=utf-8,"+encodeURIComponent(JSON.stringify(e.logs,null,2)),n=document.createElement("a");n.setAttribute("href",a),n.setAttribute("download",`logs_${new Date().getTime()}.json`),document.body.appendChild(n),n.click(),n.remove();};return e?jsxs("div",{className:"liql-panel",style:{right:"20px",bottom:"20px",width:"440px",height:"680px"},children:[jsxs("div",{className:"liql-panelHeader",children:[jsx("div",{className:"liql-titleGroup",children:jsxs("span",{className:"liql-title",children:["ReactLooger console [",e.logs.length,"]"]})}),jsxs("div",{className:"liql-controls",children:[jsx("button",{className:"liql-btnAction",onClick:p,title:"Export JSON",children:"\u{1F4E5}"}),jsx("button",{className:"liql-btnAction",onClick:c,title:"Clear Logs",children:"\u{1F5D1}\uFE0F"}),jsx("button",{className:"liql-btnAction",onClick:()=>t(false),title:"Minimize",children:"\u2796"})]})]}),jsxs("div",{className:"liql-filterBar",children:[jsx("input",{type:"text",placeholder:"Search entries...",className:"liql-searchInput",value:l,onChange:a=>s(a.target.value)}),jsxs("select",{className:"liql-selectFilter",value:r,onChange:a=>i(a.target.value),children:[jsx("option",{value:"ALL",children:"ALL"}),jsx("option",{value:"DEBUG",children:"DEBUG"}),jsx("option",{value:"ERROR",children:"ERROR"}),jsx("option",{value:"OBJECT",children:"OBJECTS"})]})]}),jsx(I,{logs:e.logs,filter:r,search:l}),jsxs("div",{className:"liql-footerActions",children:[jsx("div",{className:"liql-storageInfo",children:e.config.persistence?`DRIVER: ${e.config.persistenceDriver?.toUpperCase()}`:"SESSION MODE"}),jsx("button",{className:"liql-btnClose",onClick:()=>t(false),children:"CLOSE CONSOLE"})]})]}):null};var $e=()=>{let{isPanelOpen:e}=m();return jsxs(Fragment,{children:[jsx(D,{}),e&&jsx(P,{})]})};export{xe as LoggerProvider,$e as LoggerViewer,Le as useLogger,m as useLoggerContext};//# sourceMappingURL=index.mjs.map
3
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["#style-inject:#style-inject","../src/styles/logger.css","../src/storage/localStorageDriver.ts","../src/storage/indexedDBDriver.ts","../src/storage/index.ts","../src/context/LoggerContext.tsx","../src/hooks/useLogger.ts","../src/components/FloatingButton.tsx","../src/components/LogItem.tsx","../src/components/LogList.tsx","../src/components/LogPanel.tsx","../src/components/LoggerViewer.tsx"],"names":["styleInject","css","insertAt","head","style","LocalStorageDriver","key","logs","e","data","DB_NAME","STORE_NAME","DB_VERSION","IndexedDBDriver","openDB","db","tx","store","log","getStorageDriver","type","initialState","loggerReducer","state","action","newLogs","LoggerContext","createContext","LoggerProvider","children","config","dispatch","useReducer","isPanelOpen","setIsPanelOpen","React","storageRef","useRef","useEffect","loadedLogs","jsx","useLoggerContext","context","useContext","useLogger","addLog","useCallback","level","message","extra","debug","error","err","title","stack","object","clear","exportLogs","dataStr","downloadAnchorNode","FloatingButton","position","setPosition","useState","isDragging","dragOffset","handleMouseDown","handleTouchStart","touch","handleMove","clientX","clientY","onMouseMove","onTouchMove","onEnd","handleClick","jsxs","LogItem","isExpanded","setIsExpanded","formatTime","isoString","renderJson","getIcon","levelClass","LogList","filter","search","filteredLogs","matchesFilter","matchesSearch","Virtuoso","_index","LogPanel","setFilter","setSearch","clearLogs","LoggerViewer","Fragment"],"mappings":"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","file":"index.mjs","sourcesContent":["\n export default function styleInject(css, { insertAt } = {}) {\n if (!css || typeof document === 'undefined') return\n \n const head = document.head || document.getElementsByTagName('head')[0]\n const style = document.createElement('style')\n style.type = 'text/css'\n \n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild)\n } else {\n head.appendChild(style)\n }\n } else {\n head.appendChild(style)\n }\n \n if (style.styleSheet) {\n style.styleSheet.cssText = css\n } else {\n style.appendChild(document.createTextNode(css))\n }\n }\n ","import styleInject from '#style-inject';styleInject(\":root{--liql-primary: #3498db;--liql-primary-glow: rgba(52, 152, 219, .5);--liql-error: #ff4d4d;--liql-error-glow: rgba(255, 77, 77, .4);--liql-object: #00ff88;--liql-object-glow: rgba(0, 255, 136, .4);--liql-glass-bg: rgba(15, 23, 42, .7);--liql-glass-border: rgba(255, 255, 255, .1);--liql-glass-edge: rgba(255, 255, 255, .05);--liql-text-main: #f8fafc;--liql-text-muted: #94a3b8;--liql-font-family: \\\"Inter\\\", -apple-system, BlinkMacSystemFont, \\\"Segoe UI\\\", Roboto, sans-serif}.liql-floatingButton{position:fixed;width:60px;height:60px;border-radius:50%;background:radial-gradient(circle at 30% 30%,rgba(255,255,255,.15),transparent),linear-gradient(135deg,var(--liql-primary),#2980b9);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);box-shadow:0 8px 32px #0006,0 0 15px var(--liql-primary-glow);display:flex;align-items:center;justify-content:center;cursor:grab;z-index:10000;border:1px solid var(--liql-glass-border);transition:all .4s cubic-bezier(.175,.885,.32,1.275);font-size:26px;color:#fff;user-select:none;touch-action:none}.liql-floatingButton:hover{transform:scale(1.1) rotate(5deg);box-shadow:0 12px 40px #00000080,0 0 25px var(--liql-primary-glow)}.liql-badge{position:absolute;top:-2px;right:-2px;background:var(--liql-error);color:#fff;border-radius:12px;min-width:20px;height:20px;padding:0 6px;font-size:11px;font-weight:700;display:flex;align-items:center;justify-content:center;border:2px solid #0f172a;box-shadow:0 0 10px var(--liql-error-glow)}.liql-panel{position:fixed;background:var(--liql-glass-bg);backdrop-filter:blur(25px) saturate(180%);-webkit-backdrop-filter:blur(25px) saturate(180%);box-shadow:0 20px 50px #0009,inset 0 0 0 1px var(--liql-glass-edge);border:1px solid var(--liql-glass-border);border-radius:20px;display:flex;flex-direction:column;z-index:10001;overflow:hidden;font-family:var(--liql-font-family);color:var(--liql-text-main);animation:liql-slideUp .4s cubic-bezier(.23,1,.32,1)}.liql-panelHeader{background:#ffffff0d;padding:16px 20px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--liql-glass-border)}.liql-title{font-weight:700;letter-spacing:.5px;text-transform:uppercase;font-size:12px;color:var(--liql-text-muted)}.liql-controls{display:flex;gap:12px}.liql-btnAction{background:#ffffff14;border:1px solid var(--liql-glass-border);color:var(--liql-text-main);width:32px;height:32px;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}.liql-btnAction:hover{background:#ffffff26;transform:translateY(-2px)}.liql-filterBar{padding:12px 20px;display:flex;gap:10px;background:#0003;align-items:center}.liql-searchInput{flex:1;background:#ffffff0d;border:1px solid var(--liql-glass-border);border-radius:10px;padding:8px 14px;color:#fff;font-size:13px}.liql-searchInput:focus{outline:none;border-color:var(--liql-primary);background:#ffffff14}.liql-selectFilter{background:#ffffff0d;border:1px solid var(--liql-glass-border);border-radius:10px;padding:8px;color:#fff;font-size:12px;cursor:pointer}.liql-logListContainer{flex:1;padding:10px}.liql-logItem{margin-bottom:10px;padding:16px;border-radius:14px;background:#ffffff08;border:1px solid var(--liql-glass-border);transition:all .3s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden}.liql-logItem:hover{background:#ffffff0f;border-color:#fff3;transform:scale(1.01)}.liql-logItem-debug{border-left:4px solid var(--liql-primary)}.liql-logItem-error{border-left:4px solid var(--liql-error);box-shadow:inset 0 0 15px var(--liql-error-glow)}.liql-logItem-object{border-left:4px solid var(--liql-object);box-shadow:inset 0 0 15px var(--liql-object-glow)}.liql-logMeta{display:flex;justify-content:space-between;margin-bottom:8px;font-size:11px;font-weight:500;text-transform:uppercase}.liql-statusTag{padding:2px 8px;border-radius:6px;font-size:9px;font-weight:700}.liql-tag-debug{background:#3498db33;color:#3498db}.liql-tag-error{background:#ff4d4d33;color:#ff4d4d}.liql-tag-object{background:#0f83;color:#0f8}.liql-logTime{color:var(--liql-text-muted)}.liql-logContent{font-size:14px;line-height:1.5;color:#e2e8f0}.liql-expandedContent{margin-top:12px;padding:12px;background:#0000004d;border-radius:10px;font-family:JetBrains Mono,monospace;font-size:12px;border:1px solid var(--liql-glass-border);color:#a5f3fc;overflow-x:auto}.liql-footerActions{padding:14px 20px;background:#0003;display:flex;justify-content:space-between;align-items:center;border-top:1px solid var(--liql-glass-border)}.liql-storageInfo{font-size:10px;color:var(--liql-text-muted)}.liql-btnClose{background:linear-gradient(135deg,var(--liql-primary),#2980b9);border:none;padding:8px 16px;border-radius:8px;color:#fff;font-weight:600;font-size:13px;cursor:pointer;box-shadow:0 4px 12px var(--liql-primary-glow)}@keyframes liql-slideUp{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}@media(max-width:600px){.liql-panel{width:94%!important;height:85%!important;bottom:3%!important;right:3%!important;left:3%!important;border-radius:24px}}\\n\")","import { LogEntry, StorageDriver } from '../types';\n\nexport class LocalStorageDriver implements StorageDriver {\n private key: string;\n\n constructor(key: string = 'ionic_react_logger_logs') {\n this.key = key;\n }\n\n async save(logs: LogEntry[]): Promise<void> {\n try {\n localStorage.setItem(this.key, JSON.stringify(logs));\n } catch (e) {\n console.error('Failed to save logs to localStorage', e);\n }\n }\n\n async load(): Promise<LogEntry[]> {\n try {\n const data = localStorage.getItem(this.key);\n return data ? JSON.parse(data) : [];\n } catch (e) {\n console.error('Failed to load logs from localStorage', e);\n return [];\n }\n }\n\n async clear(): Promise<void> {\n localStorage.removeItem(this.key);\n }\n}\n","import { openDB, IDBPDatabase } from 'idb';\nimport { LogEntry, StorageDriver } from '../types';\n\nconst DB_NAME = 'IonicReactLoggerDB';\nconst STORE_NAME = 'logs';\nconst DB_VERSION = 1;\n\nexport class IndexedDBDriver implements StorageDriver {\n private dbPromise: Promise<IDBPDatabase>;\n\n constructor() {\n this.dbPromise = openDB(DB_NAME, DB_VERSION, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME, { keyPath: 'id' });\n }\n },\n });\n }\n\n async save(logs: LogEntry[]): Promise<void> {\n const db = await this.dbPromise;\n const tx = db.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n\n // Clear existing logs first or manage them as needed\n // Simple implementation: clear and re-save everything to match the expected behavior of StorageDriver\n await store.clear();\n for (const log of logs) {\n await store.put(log);\n }\n await tx.done;\n }\n\n async load(): Promise<LogEntry[]> {\n const db = await this.dbPromise;\n return db.getAll(STORE_NAME);\n }\n\n async clear(): Promise<void> {\n const db = await this.dbPromise;\n const tx = db.transaction(STORE_NAME, 'readwrite');\n await tx.objectStore(STORE_NAME).clear();\n await tx.done;\n }\n}\n","import { StorageDriver, LogEntry } from '../types';\nimport { LocalStorageDriver } from './localStorageDriver';\nimport { IndexedDBDriver } from './indexedDBDriver';\n\nexport function getStorageDriver(type: 'localStorage' | 'indexedDB'): StorageDriver {\n if (type === 'indexedDB') {\n return new IndexedDBDriver();\n }\n return new LocalStorageDriver();\n}\n\nexport * from './localStorageDriver';\nexport * from './indexedDBDriver';\n","import React, { createContext, useContext, useReducer, useEffect, useCallback, useRef } from 'react';\nimport { LogEntry, LogLevel, LoggerConfig, StorageDriver } from '../types';\nimport { getStorageDriver } from '../storage';\n\ninterface LoggerState {\n logs: LogEntry[];\n config: LoggerConfig;\n unreadCount: number;\n}\n\ntype LoggerAction =\n | { type: 'ADD_LOG'; log: LogEntry }\n | { type: 'SET_LOGS'; logs: LogEntry[] }\n | { type: 'CLEAR_LOGS' }\n | { type: 'SET_CONFIG'; config: Partial<LoggerConfig> }\n | { type: 'RESET_UNREAD' }\n | { type: 'INCREMENT_UNREAD' };\n\nconst initialState: LoggerState = {\n logs: [],\n config: {\n persistence: false,\n persistenceDriver: 'localStorage',\n maxLogs: 500,\n },\n unreadCount: 0,\n};\n\nfunction loggerReducer(state: LoggerState, action: LoggerAction): LoggerState {\n switch (action.type) {\n case 'ADD_LOG': {\n const newLogs = [action.log, ...state.logs].slice(0, state.config.maxLogs);\n return {\n ...state,\n logs: newLogs,\n };\n }\n case 'SET_LOGS':\n return { ...state, logs: action.logs };\n case 'CLEAR_LOGS':\n return { ...state, logs: [], unreadCount: 0 };\n case 'SET_CONFIG':\n return { ...state, config: { ...state.config, ...action.config } };\n case 'RESET_UNREAD':\n return { ...state, unreadCount: 0 };\n case 'INCREMENT_UNREAD':\n return { ...state, unreadCount: state.unreadCount + 1 };\n default:\n return state;\n }\n}\n\ninterface LoggerContextType {\n state: LoggerState;\n dispatch: React.Dispatch<LoggerAction>;\n isPanelOpen: boolean;\n setIsPanelOpen: (open: boolean) => void;\n}\n\nconst LoggerContext = createContext<LoggerContextType | undefined>(undefined);\n\n/**\n * Provides the logging context to all child components.\n * This should wrap your entire application (e.g. in App.tsx).\n * \n * @param config Optional initial configuration for persistence and limits.\n */\nexport const LoggerProvider: React.FC<{ children: React.ReactNode; config?: LoggerConfig }> = ({\n children,\n config,\n}) => {\n const [state, dispatch] = useReducer(loggerReducer, {\n ...initialState,\n config: { ...initialState.config, ...config },\n });\n const [isPanelOpen, setIsPanelOpen] = React.useState(false);\n const storageRef = useRef<StorageDriver | null>(null);\n\n // Initialize storage driver\n useEffect(() => {\n if (state.config.persistence) {\n storageRef.current = getStorageDriver(state.config.persistenceDriver || 'localStorage');\n storageRef.current.load().then((loadedLogs) => {\n dispatch({ type: 'SET_LOGS', logs: loadedLogs });\n });\n }\n }, [state.config.persistence, state.config.persistenceDriver]);\n\n // Persist logs when they change\n useEffect(() => {\n if (state.config.persistence && storageRef.current) {\n storageRef.current.save(state.logs);\n }\n }, [state.logs, state.config.persistence]);\n\n return (\n <LoggerContext.Provider value={{ state, dispatch, isPanelOpen, setIsPanelOpen }}>\n {children}\n </LoggerContext.Provider>\n );\n};\n\n/**\n * Internal hook to access the logger context state.\n * Use the public `useLogger` hook for standard logging operations.\n */\nexport const useLoggerContext = () => {\n const context = useContext(LoggerContext);\n if (!context) {\n throw new Error('useLoggerContext must be used within a LoggerProvider');\n }\n return context;\n};\n","import { useCallback } from 'react';\nimport { useLoggerContext } from '../context/LoggerContext';\nimport { LogEntry, LogLevel } from '../types';\n\n/**\n * Main hook to interact with the Liquid Glass Logger.\n * \n * Provides methods for different logging levels and allows accessing\n * the current state of logs.\n * \n * @example\n * ```tsx\n * const { debug, error, object } = useLogger();\n * \n * debug('App started');\n * error(new Error('Failed to fetch'), 'API Error');\n * object({ user: 'John' }, 'Current User');\n * ```\n */\nexport const useLogger = () => {\n const { state, dispatch, isPanelOpen } = useLoggerContext();\n\n const addLog = useCallback((level: LogLevel, message: string, extra?: { title?: string, data?: any, stack?: string }) => {\n const log: LogEntry = {\n id: Math.random().toString(36).substring(2, 9),\n level,\n message,\n timestamp: new Date().toISOString(),\n ...extra\n };\n\n dispatch({ type: 'ADD_LOG', log: log });\n state.config.onLogAdded?.(log);\n }, [dispatch, state.config]);\n\n /**\n * Log a debug message (Blue neon)\n */\n const debug = useCallback((message: string) => addLog('DEBUG', message), [addLog]);\n\n /**\n * Log an error. If an Error object is passed, it automatically captures the stack trace.\n * (Red neon)\n */\n const error = useCallback((err: string | Error, title?: string) => {\n const message = err instanceof Error ? err.message : err;\n const stack = err instanceof Error ? err.stack : undefined;\n addLog('ERROR', message, { title: title || 'Error', stack });\n }, [addLog]);\n\n /**\n * Log a data object for inspection.\n * (Green neon)\n */\n const object = useCallback((data: any, title?: string) => {\n addLog('OBJECT', 'Object visualization', { title: title || 'Data Object', data });\n }, [addLog]);\n\n /**\n * Clears all logs from the current session and persistent storage.\n */\n const clear = useCallback(() => {\n dispatch({ type: 'CLEAR_LOGS' });\n }, [dispatch]);\n\n /**\n * Exports all logs as a JSON file download.\n */\n const exportLogs = useCallback(() => {\n const dataStr = \"data:text/json;charset=utf-8,\" + encodeURIComponent(JSON.stringify(state.logs, null, 2));\n const downloadAnchorNode = document.createElement('a');\n downloadAnchorNode.setAttribute(\"href\", dataStr);\n downloadAnchorNode.setAttribute(\"download\", `logs_${new Date().getTime()}.json`);\n document.body.appendChild(downloadAnchorNode);\n downloadAnchorNode.click();\n downloadAnchorNode.remove();\n }, [state.logs]);\n\n return {\n /** Register a debug message */\n debug,\n /** Register an error or exception */\n error,\n /** Register an object for JSON visualization */\n object,\n /** Clear all history */\n clear,\n /** Download history as JSON */\n exportLogs,\n /** List of all captured logs */\n logs: state.logs,\n /** Count of logs not yet viewed */\n unreadCount: state.unreadCount,\n };\n};\n","import React, { useState, useRef, useEffect, useCallback } from 'react';\nimport { useLoggerContext } from '../context/LoggerContext';\n\n/**\n * Draggable floating trigger button (Bug 🐞).\n * Displays unread counts and opens the main logging console.\n */\nexport const FloatingButton: React.FC = () => {\n const { state, setIsPanelOpen } = useLoggerContext();\n const [position, setPosition] = useState({ x: window.innerWidth - 80, y: window.innerHeight - 80 });\n const isDragging = useRef(false);\n const dragOffset = useRef({ x: 0, y: 0 });\n\n const handleMouseDown = (e: React.MouseEvent) => {\n isDragging.current = true;\n dragOffset.current = {\n x: e.clientX - position.x,\n y: e.clientY - position.y\n };\n };\n\n const handleTouchStart = (e: React.TouchEvent) => {\n isDragging.current = true;\n const touch = e.touches[0];\n dragOffset.current = {\n x: touch.clientX - position.x,\n y: touch.clientY - position.y\n };\n };\n\n const handleMove = useCallback((clientX: number, clientY: number) => {\n if (!isDragging.current) return;\n\n setPosition({\n x: Math.max(0, Math.min(window.innerWidth - 60, clientX - dragOffset.current.x)),\n y: Math.max(0, Math.min(window.innerHeight - 60, clientY - dragOffset.current.y))\n });\n }, []);\n\n useEffect(() => {\n const onMouseMove = (e: MouseEvent) => handleMove(e.clientX, e.clientY);\n const onTouchMove = (e: TouchEvent) => handleMove(e.touches[0].clientX, e.touches[0].clientY);\n const onEnd = () => (isDragging.current = false);\n\n window.addEventListener('mousemove', onMouseMove);\n window.addEventListener('touchmove', onTouchMove);\n window.addEventListener('mouseup', onEnd);\n window.addEventListener('touchend', onEnd);\n\n return () => {\n window.removeEventListener('mousemove', onMouseMove);\n window.removeEventListener('touchmove', onTouchMove);\n window.removeEventListener('mouseup', onEnd);\n window.removeEventListener('touchend', onEnd);\n };\n }, [handleMove]);\n\n const handleClick = () => {\n if (isDragging.current) return;\n setIsPanelOpen(true);\n };\n\n return (\n <div\n className=\"liql-floatingButton\"\n style={{ left: `${position.x}px`, top: `${position.y}px` }}\n onMouseDown={handleMouseDown}\n onTouchStart={handleTouchStart}\n onClick={handleClick}\n aria-label=\"Open Logger\"\n >\n 🐞\n {state.unreadCount > 0 && (\n <div className=\"liql-badge\">{state.unreadCount}</div>\n )}\n </div>\n );\n};\n","import React, { useState } from 'react';\nimport { LogEntry } from '../types';\n\ninterface LogItemProps {\n log: LogEntry;\n}\n\nexport const LogItem: React.FC<LogItemProps> = ({ log }) => {\n const [isExpanded, setIsExpanded] = useState(false);\n\n const formatTime = (isoString: string) => {\n const date = new Date(isoString);\n return date.toTimeString().split(' ')[0];\n };\n\n const renderJson = (data: any) => {\n try {\n return (\n <pre className=\"liql-expandedContent\">\n {JSON.stringify(data, null, 2)}\n </pre>\n );\n } catch (e) {\n return <div>Error parsing object</div>;\n }\n };\n\n const getIcon = (level: string) => {\n switch (level) {\n case 'DEBUG': return '🐞';\n case 'ERROR': return '❌';\n case 'OBJECT': return '📦';\n default: return '📝';\n }\n };\n\n const levelClass = log.level.toLowerCase();\n\n return (\n <div\n className={`liql-logItem liql-logItem-${levelClass}`}\n onClick={() => setIsExpanded(!isExpanded)}\n >\n <div className=\"liql-logMeta\">\n <span className={`liql-statusTag liql-tag-${levelClass}`}>\n {getIcon(log.level)} {log.level}\n </span>\n <span className=\"liql-logTime\">{formatTime(log.timestamp)}</span>\n </div>\n <div className=\"liql-logContent\">\n {log.title && <strong style={{ color: 'var(--liql-primary)' }}>{log.title}: </strong>}\n {log.message}\n </div>\n\n {isExpanded && (\n <div className=\"liql-expandedContent\">\n {log.stack && (\n <div style={{ color: 'var(--liql-error)', marginBottom: '8px' }}>\n <strong>Stack Trace:</strong>\n <pre style={{ whiteSpace: 'pre-wrap', fontSize: '10px', marginTop: '4px' }}>{log.stack}</pre>\n </div>\n )}\n {log.data && (\n <div>\n <strong>Payload:</strong>\n {renderJson(log.data)}\n </div>\n )}\n {!log.data && !log.stack && <div>Full message: {log.message}</div>}\n </div>\n )}\n </div>\n );\n};\n","import React from 'react';\nimport { Virtuoso } from 'react-virtuoso';\nimport { LogItem } from './LogItem';\nimport { LogEntry } from '../types';\n\ninterface LogListProps {\n logs: LogEntry[];\n filter: string;\n search: string;\n}\n\nexport const LogList: React.FC<LogListProps> = ({ logs, filter, search }) => {\n const filteredLogs = React.useMemo(() => {\n return logs.filter(log => {\n const matchesFilter = filter === 'ALL' || log.level === filter;\n const matchesSearch = search === '' ||\n log.message.toLowerCase().includes(search.toLowerCase()) ||\n (log.title || '').toLowerCase().includes(search.toLowerCase());\n return matchesFilter && matchesSearch;\n }).reverse();\n }, [logs, filter, search]);\n\n return (\n <div className=\"liql-logListContainer\">\n <Virtuoso\n style={{ height: '100%' }}\n data={filteredLogs}\n itemContent={(_index: number, log: LogEntry) => <LogItem key={log.id} log={log} />}\n />\n </div>\n );\n};\n","import React, { useState } from 'react';\nimport { useLoggerContext } from '../context/LoggerContext';\nimport { LogList } from './LogList';\n\n/**\n * Main 'Liquid Glass' logging console.\n * Contains the log list, filtering, search, and action controls.\n */\nexport const LogPanel: React.FC = () => {\n const { state, setIsPanelOpen, dispatch } = useLoggerContext();\n const [filter, setFilter] = useState('ALL');\n const [search, setSearch] = useState('');\n\n const clearLogs = () => {\n if (confirm('Are you sure you want to clear all logs?')) {\n dispatch({ type: 'CLEAR_LOGS' });\n }\n };\n\n const exportLogs = () => {\n const dataStr = \"data:text/json;charset=utf-8,\" + encodeURIComponent(JSON.stringify(state.logs, null, 2));\n const downloadAnchorNode = document.createElement('a');\n downloadAnchorNode.setAttribute(\"href\", dataStr);\n downloadAnchorNode.setAttribute(\"download\", `logs_${new Date().getTime()}.json`);\n document.body.appendChild(downloadAnchorNode);\n downloadAnchorNode.click();\n downloadAnchorNode.remove();\n };\n\n if (!state) return null;\n\n return (\n <div className=\"liql-panel\" style={{ right: '20px', bottom: '20px', width: '440px', height: '680px' }}>\n <div className=\"liql-panelHeader\">\n <div className=\"liql-titleGroup\">\n <span className=\"liql-title\">ReactLooger console [{state.logs.length}]</span>\n </div>\n <div className=\"liql-controls\">\n <button className=\"liql-btnAction\" onClick={exportLogs} title=\"Export JSON\">📥</button>\n <button className=\"liql-btnAction\" onClick={clearLogs} title=\"Clear Logs\">🗑️</button>\n <button className=\"liql-btnAction\" onClick={() => setIsPanelOpen(false)} title=\"Minimize\">➖</button>\n </div>\n </div>\n\n <div className=\"liql-filterBar\">\n <input\n type=\"text\"\n placeholder=\"Search entries...\"\n className=\"liql-searchInput\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n <select\n className=\"liql-selectFilter\"\n value={filter}\n onChange={(e) => setFilter(e.target.value)}\n >\n <option value=\"ALL\">ALL</option>\n <option value=\"DEBUG\">DEBUG</option>\n <option value=\"ERROR\">ERROR</option>\n <option value=\"OBJECT\">OBJECTS</option>\n </select>\n </div>\n\n <LogList logs={state.logs} filter={filter} search={search} />\n\n <div className=\"liql-footerActions\">\n <div className=\"liql-storageInfo\">\n {state.config.persistence ? `DRIVER: ${state.config.persistenceDriver?.toUpperCase()}` : 'SESSION MODE'}\n </div>\n <button className=\"liql-btnClose\" onClick={() => setIsPanelOpen(false)}>CLOSE CONSOLE</button>\n </div>\n </div>\n );\n};\n","import React from 'react';\nimport { useLoggerContext } from '../context/LoggerContext';\nimport { FloatingButton } from './FloatingButton';\nimport { LogPanel } from './LogPanel';\n\n/**\n * Main entry point for the Logger UI.\n * Renders the floating trigger button and conditionally shows the glass panel.\n * Should be placed at the root level of your application.\n */\nexport const LoggerViewer: React.FC = () => {\n const { isPanelOpen } = useLoggerContext();\n\n return (\n <>\n <FloatingButton />\n {isPanelOpen && <LogPanel />}\n </>\n );\n};\n"]}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "react-looger",
3
+ "version": "1.0.0",
4
+ "description": "A professional visual logging system for Ionic React applications.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsup",
13
+ "dev": "tsup --watch",
14
+ "lint": "eslint src --ext .ts,.tsx",
15
+ "test": "jest",
16
+ "typecheck": "tsc --noEmit"
17
+ },
18
+ "keywords": [
19
+ "ionic",
20
+ "react",
21
+ "logger",
22
+ "debug",
23
+ "console"
24
+ ],
25
+ "author": "ReactLooger Team",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/raysdl9012/react-logger.git"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/raysdl9012/react-logger/issues"
33
+ },
34
+ "homepage": "https://github.com/raysdl9012/react-logger#readme",
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "peerDependencies": {
39
+ "react": ">=18.0.0",
40
+ "react-dom": ">=18.0.0"
41
+ },
42
+ "dependencies": {
43
+ "idb": "^8.0.0",
44
+ "react-virtuoso": "^4.7.0"
45
+ },
46
+ "devDependencies": {
47
+ "@testing-library/react": "^14.0.0",
48
+ "@types/jest": "^29.5.0",
49
+ "@types/react": "^18.0.0",
50
+ "@types/react-dom": "^18.0.0",
51
+ "eslint": "^8.40.0",
52
+ "identity-obj-proxy": "^3.0.0",
53
+ "jest": "^29.5.0",
54
+ "jest-environment-jsdom": "^30.2.0",
55
+ "ts-jest": "^29.1.0",
56
+ "tsup": "^8.0.0",
57
+ "typescript": "^5.0.0"
58
+ }
59
+ }