@journyio/messaging-sdk 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 +256 -0
- package/dist/index.d.ts +6 -0
- package/dist/journy-messages.css +1 -0
- package/dist/journy-messages.esm.js +2833 -0
- package/dist/journy-messages.js +2860 -0
- package/dist/journy-messages.min.js +4 -0
- package/dist/types.d.ts +42 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# @journyio/messaging-sdk
|
|
2
|
+
|
|
3
|
+
A standalone JavaScript library for displaying in-app messages from Journy. This library can be integrated via a `<script>` tag, ES modules, or npm package, similar to analytics libraries like Segment or Google Analytics.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Standalone Library**: Works without a build step in the host application
|
|
8
|
+
- ⚛️ **React Support**: Automatically renders React components when React is available
|
|
9
|
+
- 🔒 **XSS Protection**: Built-in HTML sanitization using DOMPurify
|
|
10
|
+
- 📦 **Lightweight**: Minimal dependencies, small bundle size
|
|
11
|
+
- 🎨 **Customizable**: Flexible styling and message types
|
|
12
|
+
- 📊 **Event Tracking**: Automatic tracking of message interactions
|
|
13
|
+
- 🔄 **Message Queue**: Smart queue management with priority support
|
|
14
|
+
- ⏱️ **Auto-polling**: Automatically fetches new messages
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
### Method 1: Script Tag (Simplest)
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<!DOCTYPE html>
|
|
22
|
+
<html>
|
|
23
|
+
<head>
|
|
24
|
+
<title>My App</title>
|
|
25
|
+
</head>
|
|
26
|
+
<body>
|
|
27
|
+
<!-- Your app content -->
|
|
28
|
+
|
|
29
|
+
<!-- Load React and ReactDOM (if not already loaded) -->
|
|
30
|
+
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
31
|
+
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
32
|
+
|
|
33
|
+
<!-- Load Journy Messages Library -->
|
|
34
|
+
<script src="https://cdn.journy.io/messages/journy-messages.min.js"></script>
|
|
35
|
+
|
|
36
|
+
<script>
|
|
37
|
+
// Initialize
|
|
38
|
+
const messaging = new JournyMessages({
|
|
39
|
+
writeKey: 'your-write-key',
|
|
40
|
+
userId: 'user-123',
|
|
41
|
+
entityType: 'user',
|
|
42
|
+
});
|
|
43
|
+
</script>
|
|
44
|
+
</body>
|
|
45
|
+
</html>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Method 2: npm Package
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install @journyio/messaging-sdk
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
import { JournyMessaging } from '@journyio/messaging-sdk';
|
|
56
|
+
|
|
57
|
+
const messaging = new JournyMessaging({
|
|
58
|
+
writeKey: 'your-write-key',
|
|
59
|
+
userId: 'user-123',
|
|
60
|
+
entityType: 'user',
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Method 3: ES Modules
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
import JournyMessages from '@journyio/messaging-sdk';
|
|
68
|
+
|
|
69
|
+
const messaging = new JournyMessages({
|
|
70
|
+
writeKey: 'your-write-key',
|
|
71
|
+
userId: 'user-123',
|
|
72
|
+
entityType: 'user',
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Configuration
|
|
77
|
+
|
|
78
|
+
### Basic Configuration
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const messaging = new JournyMessaging({
|
|
82
|
+
writeKey: 'your-write-key', // Required: Your Journy write key
|
|
83
|
+
userId: 'user-123', // Optional: User ID
|
|
84
|
+
accountId: 'account-456', // Optional: Account ID
|
|
85
|
+
entityType: 'user', // Required: 'user' or 'account'
|
|
86
|
+
apiEndpoint: 'https://jtm.journy.io', // Optional: API base URL
|
|
87
|
+
pollingInterval: 30000, // Optional: Polling interval in ms (default: 30000)
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Configuration Options
|
|
92
|
+
|
|
93
|
+
| Option | Type | Required | Default | Description |
|
|
94
|
+
|--------|------|----------|---------|-------------|
|
|
95
|
+
| `writeKey` | `string` | Yes | - | Your Journy write key for authentication |
|
|
96
|
+
| `userId` | `string` | No | - | Current user ID |
|
|
97
|
+
| `accountId` | `string` | No | - | Current account ID |
|
|
98
|
+
| `entityType` | `'user' \| 'account'` | Yes | - | Type of entity to fetch messages for |
|
|
99
|
+
| `apiEndpoint` | `string` | No | `'https://jtm.journy.io'` | API base URL |
|
|
100
|
+
| `pollingInterval` | `number` | No | `30000` | Interval in milliseconds to poll for new messages |
|
|
101
|
+
|
|
102
|
+
## API Reference
|
|
103
|
+
|
|
104
|
+
### Methods
|
|
105
|
+
|
|
106
|
+
#### `markAsRead(messageId: string): Promise<void>`
|
|
107
|
+
|
|
108
|
+
Marks a message as read and removes it from the queue.
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
await messaging.markAsRead('message-123');
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### `trackLinkClick(messageId: string, linkUrl: string): void`
|
|
115
|
+
|
|
116
|
+
Tracks when a user clicks a link in a message.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
messaging.trackLinkClick('message-123', 'https://example.com');
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### `trackMessageClosed(messageId: string): void`
|
|
123
|
+
|
|
124
|
+
Tracks when a user closes a message.
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
messaging.trackMessageClosed('message-123');
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### `trackMessageOpened(messageId: string): void`
|
|
131
|
+
|
|
132
|
+
Tracks when a message is opened/displayed.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
messaging.trackMessageOpened('message-123');
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### `destroy(): void`
|
|
139
|
+
|
|
140
|
+
Cleans up the messaging instance, stops polling, and removes UI elements.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
messaging.destroy();
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Message Format
|
|
147
|
+
|
|
148
|
+
Messages from the API should follow this format:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
interface Message {
|
|
152
|
+
id: string;
|
|
153
|
+
content: string; // HTML content (will be sanitized)
|
|
154
|
+
title?: string; // Optional title
|
|
155
|
+
type?: 'info' | 'success' | 'warning' | 'error';
|
|
156
|
+
priority?: number; // Higher numbers = higher priority
|
|
157
|
+
createdAt: string; // ISO 8601 timestamp
|
|
158
|
+
expiresAt?: string; // ISO 8601 timestamp
|
|
159
|
+
actions?: MessageAction[]; // Optional action buttons
|
|
160
|
+
metadata?: Record<string, any>;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
interface MessageAction {
|
|
164
|
+
label: string;
|
|
165
|
+
url?: string;
|
|
166
|
+
action?: string;
|
|
167
|
+
style?: 'primary' | 'secondary' | 'link';
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Styling
|
|
172
|
+
|
|
173
|
+
### Default styles
|
|
174
|
+
|
|
175
|
+
When you do not pass a `styles` option (or set `styles: 'default'`), the SDK **injects the default styles automatically**. You do not need to add a `<link>` tag; a single script tag is enough for the widget to look correct.
|
|
176
|
+
|
|
177
|
+
If you prefer to load the CSS yourself (e.g. for caching), you can still link the built file and set `styles: 'none'` then include `journy-messages.css` in your page—but the typical use is to omit `styles` and let the SDK inject the default CSS.
|
|
178
|
+
|
|
179
|
+
### Configurable styles
|
|
180
|
+
|
|
181
|
+
You can control styling via the `styles` config option:
|
|
182
|
+
|
|
183
|
+
- **`styles: 'default'` or omitted** – The SDK injects the default styles inline. No separate CSS file needed.
|
|
184
|
+
- **`styles: 'none'`** – No SDK styles are injected. You provide all CSS (e.g. target `.journy-message-widget`, `.journy-message-popup`, etc.) in your own stylesheet.
|
|
185
|
+
- **`styles: { url: 'https://...' }`** – The SDK injects a `<link rel="stylesheet" href="...">` pointing to your stylesheet.
|
|
186
|
+
- **`styles: { css: '.journy-message-widget { ... }' }`** – The SDK injects a `<style>` tag with the given CSS.
|
|
187
|
+
|
|
188
|
+
Example with custom stylesheet URL:
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
const messaging = new JournyMessages({
|
|
192
|
+
writeKey: 'your-write-key',
|
|
193
|
+
entityType: 'user',
|
|
194
|
+
styles: { url: 'https://my-app.com/journy-messages-theme.css' },
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Example with no library styles (you style everything):
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
const messaging = new JournyMessages({
|
|
202
|
+
writeKey: 'your-write-key',
|
|
203
|
+
entityType: 'user',
|
|
204
|
+
styles: 'none',
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
See `examples/alternative-styles.html` and `examples/alternative-styles.css` for a test theme and `styles: { url: '...' }` usage.
|
|
209
|
+
|
|
210
|
+
### Overriding default styles
|
|
211
|
+
|
|
212
|
+
When using default styles, you can still customize by overriding these CSS classes in your own CSS:
|
|
213
|
+
|
|
214
|
+
- `.journy-message-overlay` - The backdrop overlay
|
|
215
|
+
- `.journy-message-popup` - The message popup container
|
|
216
|
+
- `.journy-message-title` - Message title
|
|
217
|
+
- `.journy-message-content` - Message content area
|
|
218
|
+
- `.journy-message-close` - Close button
|
|
219
|
+
- `.journy-message-actions` - Action buttons container
|
|
220
|
+
- `.journy-message-action` - Individual action button
|
|
221
|
+
|
|
222
|
+
### Message Type Classes
|
|
223
|
+
|
|
224
|
+
- `.journy-message-info` - Info messages (blue border)
|
|
225
|
+
- `.journy-message-success` - Success messages (green border)
|
|
226
|
+
- `.journy-message-warning` - Warning messages (orange border)
|
|
227
|
+
- `.journy-message-error` - Error messages (red border)
|
|
228
|
+
|
|
229
|
+
## Security
|
|
230
|
+
|
|
231
|
+
### XSS Prevention
|
|
232
|
+
|
|
233
|
+
The library uses [DOMPurify](https://github.com/cure53/DOMPurify) to sanitize all HTML content before rendering. Only the following HTML tags and attributes are allowed:
|
|
234
|
+
|
|
235
|
+
**Allowed Tags:**
|
|
236
|
+
- `a`, `b`, `i`, `em`, `strong`, `p`, `br`, `ul`, `ol`, `li`
|
|
237
|
+
|
|
238
|
+
**Allowed Attributes:**
|
|
239
|
+
- `href`, `target`, `rel` (for links)
|
|
240
|
+
|
|
241
|
+
All other HTML is stripped to prevent XSS attacks.
|
|
242
|
+
|
|
243
|
+
## Browser Support
|
|
244
|
+
|
|
245
|
+
- Chrome (latest)
|
|
246
|
+
- Firefox (latest)
|
|
247
|
+
- Safari (latest)
|
|
248
|
+
- Edge (latest)
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT
|
|
253
|
+
|
|
254
|
+
## Support
|
|
255
|
+
|
|
256
|
+
For issues and questions, please visit [GitHub Issues](https://github.com/journy-io/journy-in-app-messages/issues).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { JournyMessaging } from './core/JournyMessaging';
|
|
2
|
+
export { JournyMessaging };
|
|
3
|
+
export type { JournyMessagingConfig } from './core/JournyMessaging';
|
|
4
|
+
export type { Message, AppDisplayMode, StylesConfig } from './types';
|
|
5
|
+
export default JournyMessaging;
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.journy-message-widget{background:#fff;border-radius:12px;box-shadow:0 4px 20px rgba(0,0,0,.15);overflow:hidden;position:fixed;transform-origin:bottom right;user-select:none;z-index:10000}.journy-message-widget.journy-message-widget-dragging{cursor:grabbing;transition:none;z-index:10001}.journy-message-widget.journy-message-widget-expanded{display:flex;flex-direction:column;min-height:0}.journy-message-widget.journy-message-widget-resizing{transition:none}.journy-message-widget-header{align-items:center;background:#f9fafb;border-bottom:1px solid #e5e7eb;display:flex;justify-content:space-between;padding:12px 16px;user-select:none}.journy-message-widget-drag-handle{align-items:center;color:#9ca3af;cursor:grab;display:flex;flex-shrink:0;justify-content:center;margin-right:8px;padding:4px 8px;transition:color .2s}.journy-message-widget-drag-handle:active{color:#6b7280;cursor:grabbing}.journy-message-widget-drag-handle:hover{color:#6b7280}.journy-message-widget-drag-handle svg{display:block}.journy-message-widget-header-content{align-items:center;cursor:pointer;display:flex;flex:1;flex-wrap:wrap;gap:10px}.journy-message-widget-badge{align-items:center;background:#3b82f6;border-radius:12px;color:#fff;display:inline-flex;font-size:12px;font-weight:600;height:24px;justify-content:center;min-width:24px;padding:0 8px}.journy-message-widget-title{color:#111827;font-size:14px;font-weight:600}.journy-message-widget-read-count{color:#6b7280;font-size:12px;font-weight:400}.journy-message-widget-controls{align-items:center;display:flex;gap:8px}.journy-message-widget-close,.journy-message-widget-toggle{align-items:center;background:none;border:none;border-radius:4px;color:#6b7280;cursor:pointer;display:flex;font-size:18px;height:28px;justify-content:center;line-height:1;padding:4px 8px;transition:background-color .2s,color .2s;width:28px}.journy-message-widget-close:hover,.journy-message-widget-toggle:hover{background-color:#e5e7eb;color:#374151}.journy-message-widget-content{overflow-x:hidden;overflow-y:auto;padding:16px}.journy-message-widget-content,.journy-message-widget-content--single .journy-message-widget-message{display:flex;flex:1;flex-direction:column;min-height:0}.journy-message-widget-content--single .journy-message-widget-message .journy-message-content{flex:1;min-height:0;overflow-y:auto}.journy-message-widget-resize-handle{align-items:center;background:linear-gradient(135deg,transparent,transparent 40%,#e5e7eb 0,#e5e7eb);bottom:0;cursor:nwse-resize;display:flex;height:20px;justify-content:center;position:absolute;right:0;transition:background .2s;width:20px;z-index:10}.journy-message-widget-resize-handle:hover{background:linear-gradient(135deg,transparent,transparent 40%,#d1d5db 0,#d1d5db)}.journy-message-widget-resize-handle svg{height:12px;opacity:.6;width:12px}.journy-message-widget-resize-handle:hover svg{opacity:1}.journy-message-widget-message{min-height:60px;padding:0;position:relative}.journy-message-widget-message-close{align-items:center;background:transparent;border:none;border-radius:4px;color:#6b7280;cursor:pointer;display:flex;font-size:18px;height:24px;justify-content:center;line-height:1;padding:0;position:absolute;right:4px;top:4px;transition:background-color .2s,color .2s;width:24px;z-index:1}.journy-message-widget-message-close:hover{background-color:#e5e7eb;color:#374151}.journy-message-widget-message:has(.journy-message-widget-message-close) .journy-message-content{padding-right:28px}.journy-message-widget-nav{align-items:center;background:none;border:none;border-radius:4px;color:#6b7280;cursor:pointer;display:flex;font-size:18px;height:28px;justify-content:center;line-height:1;padding:4px 6px;transition:background-color .2s,color .2s;width:28px}.journy-message-widget-nav:hover:not(:disabled){background-color:#e5e7eb;color:#374151}.journy-message-widget-nav:disabled{cursor:default;opacity:.4}.journy-message-widget-message-separated{border-bottom:1px solid #e5e7eb;margin-bottom:24px;padding-bottom:24px}.journy-message-widget-message-count{color:#6b7280;font-size:12px;font-weight:400;margin-left:8px}.journy-message-widget-position{color:#9ca3af;flex-shrink:0;font-size:11px;font-weight:500;padding:4px 12px;text-align:right}.journy-message-widget-message.journy-message-info{border-left:4px solid #3b82f6;padding-left:12px}.journy-message-widget-message.journy-message-success{border-left:4px solid #10b981;padding-left:12px}.journy-message-widget-message.journy-message-warning{border-left:4px solid #f59e0b;padding-left:12px}.journy-message-widget-message.journy-message-error{border-left:4px solid #ef4444;padding-left:12px}.journy-message-widget-message.journy-message-viewed{border-left:4px solid #6b7280;padding-left:12px}.journy-message-modal-overlay{align-items:center;background-color:rgba(0,0,0,.5);bottom:0;display:flex;justify-content:center;left:0;padding:20px;position:fixed;right:0;top:0;z-index:10002}.journy-message-modal{background:#fff;border-radius:12px;box-shadow:0 4px 24px rgba(0,0,0,.2);display:flex;flex-direction:column;height:60vh;max-height:60vh;max-width:80vw;overflow:hidden;position:relative;width:80vw}.journy-message-modal-header{align-items:center;background:#f9fafb;border-bottom:1px solid #e5e7eb;display:flex;flex-shrink:0;justify-content:space-between;padding:16px 24px}.journy-message-modal-title{color:#111827;font-size:18px;font-weight:600}.journy-message-modal-close{background:none;border:none;border-radius:4px;color:#6b7280;cursor:pointer;font-size:24px;line-height:1;padding:4px 8px;transition:background-color .2s,color .2s}.journy-message-modal-close:hover{background-color:#e5e7eb;color:#374151}.journy-message-modal .journy-message-widget-message{flex:1;min-height:0;overflow-y:auto;padding:24px}.journy-message-timestamp{color:#6b7280;font-size:12px;line-height:1.4;margin-top:4px}.journy-message-content-clickable{cursor:pointer}.journy-message-content-clickable:hover{opacity:.95}.journy-message-overlay{align-items:center;background-color:rgba(0,0,0,.5);bottom:0;display:flex;justify-content:center;left:0;opacity:0;pointer-events:none;position:fixed;right:0;top:0;transition:opacity .3s ease-in-out;z-index:10000}.journy-message-overlay.journy-message-visible{opacity:1;pointer-events:all}.journy-message-popup{background:#fff;border-radius:8px;box-shadow:0 4px 20px rgba(0,0,0,.15);max-height:80vh;max-width:500px;overflow-y:auto;padding:24px;position:relative;transform:scale(.9);transition:transform .3s ease-in-out;width:90%}.journy-message-overlay.journy-message-visible .journy-message-popup{transform:scale(1)}.journy-message-popup.journy-message-info{border-top:4px solid #3b82f6}.journy-message-popup.journy-message-success{border-top:4px solid #10b981}.journy-message-popup.journy-message-warning{border-top:4px solid #f59e0b}.journy-message-popup.journy-message-error{border-top:4px solid #ef4444}.journy-message-popup.journy-message-viewed{border-top:4px solid #6b7280}.journy-message-close{background:none;border:none;border-radius:4px;color:#6b7280;cursor:pointer;font-size:24px;line-height:1;padding:4px 8px;position:absolute;right:12px;top:12px;transition:background-color .2s,color .2s}.journy-message-close:hover{background-color:#f3f4f6;color:#374151}.journy-message-title{color:#111827;font-size:20px;font-weight:600;margin-bottom:12px}.journy-message-content{color:#374151;font-size:16px;line-height:1.6;margin-bottom:16px}.journy-message-content a{color:#3b82f6;text-decoration:underline;transition:color .2s}.journy-message-content a:hover{color:#2563eb}.journy-message-content p{margin:0 0 12px}.journy-message-content p:last-child{margin-bottom:0}.journy-message-content ol,.journy-message-content ul{margin:12px 0;padding-left:24px}.journy-message-content li{margin-bottom:8px}.journy-message-content h1,.journy-message-content h2,.journy-message-content h3,.journy-message-content h4,.journy-message-content h5,.journy-message-content h6{color:#111827;font-weight:600;line-height:1.3;margin:16px 0 12px}.journy-message-content h1{font-size:24px}.journy-message-content h2{font-size:20px}.journy-message-content h3{font-size:18px}.journy-message-content h4{font-size:16px}.journy-message-content h5,.journy-message-content h6{font-size:14px}.journy-message-content code{background-color:#f3f4f6;border-radius:4px;color:#ef4444;font-size:.9em;padding:2px 6px}.journy-message-content code,.journy-message-content pre{font-family:Monaco,Menlo,Ubuntu Mono,Consolas,source-code-pro,monospace}.journy-message-content pre{background-color:#1f2937;border-radius:8px;color:#f9fafb;font-size:14px;line-height:1.5;margin:12px 0;overflow-x:auto;padding:16px}.journy-message-content pre code{background-color:transparent;border-radius:0;color:inherit;font-size:inherit;padding:0}.journy-message-content u{text-decoration:underline}.journy-message-content del,.journy-message-content s,.journy-message-content strike{text-decoration:line-through}.journy-message-content strong{font-weight:600}.journy-message-content em{font-style:italic}.journy-message-content blockquote{border-left:4px solid #e5e7eb;color:#6b7280;font-style:italic;margin:12px 0;padding-left:16px}.journy-message-content table{border-collapse:collapse;margin:12px 0;width:100%}.journy-message-content td,.journy-message-content th{border:1px solid #e5e7eb;padding:8px 12px;text-align:left}.journy-message-content th{background-color:#f9fafb;font-weight:600}.journy-message-content hr{border:none;border-top:1px solid #e5e7eb;margin:16px 0}.journy-message-actions{display:flex;flex-wrap:wrap;gap:12px;margin-top:20px}.journy-message-action{border:none;border-radius:6px;cursor:pointer;font-size:14px;font-weight:500;padding:10px 20px;transition:all .2s}.journy-message-action-primary{background-color:#3b82f6;color:#fff}.journy-message-action-primary:hover{background-color:#2563eb}.journy-message-action-secondary{background-color:#e5e7eb;color:#374151}.journy-message-action-secondary:hover{background-color:#d1d5db}.journy-message-action-link{background:none;color:#3b82f6;padding:10px 0;text-decoration:underline}.journy-message-action-link:hover{color:#2563eb}.journy-settings-panel{background:#fff;bottom:0;display:flex;flex-direction:column;position:absolute;right:0;top:0;transform:translateX(100%);transition:transform .3s ease;width:100%;z-index:10}.journy-settings-panel-open{transform:translateX(0)}.journy-settings-panel-closed{transform:translateX(100%)}.journy-settings-header{align-items:center;background:#f9fafb;border-bottom:1px solid #e5e7eb;display:flex;flex-shrink:0;justify-content:space-between;padding:12px 16px}.journy-settings-title{color:#111827;font-size:14px;font-weight:600}.journy-settings-close{align-items:center;background:none;border:none;border-radius:4px;color:#6b7280;cursor:pointer;display:flex;font-size:18px;height:28px;justify-content:center;line-height:1;padding:4px 8px;transition:background-color .2s,color .2s;width:28px}.journy-settings-close:hover{background-color:#e5e7eb;color:#374151}.journy-settings-body{flex:1;overflow-y:auto;padding:16px}.journy-settings-item{align-items:center;border-bottom:1px solid #f3f4f6;display:flex;justify-content:space-between;padding:12px 0}.journy-settings-item:last-child{border-bottom:none}.journy-settings-label{color:#374151;font-size:13px;font-weight:500}.journy-settings-select{background:#fff;border:1px solid #d1d5db;border-radius:6px;color:#374151;cursor:pointer;font-size:13px;outline:none;padding:6px 10px;transition:border-color .2s}.journy-settings-select:focus{border-color:#3b82f6}.journy-settings-toggle{background:#d1d5db;border:none;border-radius:11px;cursor:pointer;flex-shrink:0;height:22px;padding:0;position:relative;transition:background-color .2s;width:40px}.journy-settings-toggle-on{background:#3b82f6}.journy-settings-toggle-knob{background:#fff;border-radius:50%;box-shadow:0 1px 3px rgba(0,0,0,.15);height:18px;left:2px;position:absolute;top:2px;transition:transform .2s;width:18px}.journy-settings-toggle-on .journy-settings-toggle-knob{transform:translateX(18px)}.journy-settings-item-vertical{align-items:flex-start;flex-direction:column;gap:6px}.journy-settings-input{background:#fff;border:1px solid #d1d5db;border-radius:6px;box-sizing:border-box;color:#374151;font-size:13px;outline:none;padding:6px 10px;transition:border-color .2s;width:100%}.journy-settings-input:focus{border-color:#3b82f6}.journy-settings-input::placeholder{color:#9ca3af}.journy-settings-value{color:#6b7280;font-size:13px;font-weight:400;max-width:60%;text-align:right;word-break:break-all}.journy-message-widget-empty{align-items:center;color:#9ca3af;display:flex;flex:1;font-size:14px;justify-content:center;min-height:80px}.journy-settings-advanced-btn{background:none;border:none;color:#6b7280;cursor:pointer;font-size:13px;font-weight:500;padding:0;transition:color .2s}.journy-settings-advanced-btn:hover{color:#374151}.journy-message-widget-settings-btn{align-items:center;background:none;border:none;border-radius:4px;color:#6b7280;cursor:pointer;display:flex;font-size:16px;height:28px;justify-content:center;line-height:1;padding:4px 8px;transition:background-color .2s,color .2s;width:28px}.journy-message-widget-settings-btn:hover{background-color:#e5e7eb;color:#374151}@media (max-width:640px){.journy-message-widget{left:16px!important;max-width:calc(100vw - 32px);min-width:calc(100vw - 32px);right:16px!important}.journy-message-popup{padding:20px;width:95%}.journy-message-title{font-size:18px}.journy-message-content{font-size:14px}.journy-message-actions{flex-direction:column}.journy-message-action{width:100%}}
|