@customviews-js/customviews 1.4.1-beta.0 → 1.4.1-beta.1
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/dist/custom-views.core.cjs.js +277 -185
- package/dist/custom-views.core.cjs.js.map +1 -1
- package/dist/custom-views.core.esm.js +277 -185
- package/dist/custom-views.core.esm.js.map +1 -1
- package/dist/custom-views.esm.js +277 -185
- package/dist/custom-views.esm.js.map +1 -1
- package/dist/custom-views.js +277 -185
- package/dist/custom-views.js.map +1 -1
- package/dist/custom-views.min.js +2 -2
- package/dist/custom-views.min.js.map +1 -1
- package/dist/types/core/core.d.ts +4 -0
- package/dist/types/core/core.d.ts.map +1 -1
- package/dist/types/core/widget.d.ts +11 -12
- package/dist/types/core/widget.d.ts.map +1 -1
- package/dist/types/styles/widget-styles.d.ts +1 -1
- package/dist/types/styles/widget-styles.d.ts.map +1 -1
- package/dist/types/types/types.d.ts +0 -2
- package/dist/types/types/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @customviews-js/customviews v1.4.1-beta.
|
|
2
|
+
* @customviews-js/customviews v1.4.1-beta.1
|
|
3
3
|
* (c) 2025 Chan Ger Teck
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -704,7 +704,7 @@ class TabManager {
|
|
|
704
704
|
});
|
|
705
705
|
}
|
|
706
706
|
// Add tooltip for UX feedback (use native title attribute)
|
|
707
|
-
navLink.setAttribute('title',
|
|
707
|
+
navLink.setAttribute('title', "Double-click a tab to 'pin' it in all similar tab groups.");
|
|
708
708
|
listItem.appendChild(navLink);
|
|
709
709
|
navContainer.appendChild(listItem);
|
|
710
710
|
});
|
|
@@ -2732,6 +2732,12 @@ class CustomViewsCore {
|
|
|
2732
2732
|
this.componentRegistry.tabGroups.delete(tabGroup);
|
|
2733
2733
|
});
|
|
2734
2734
|
}
|
|
2735
|
+
/**
|
|
2736
|
+
* Check if there are any active components in the registry
|
|
2737
|
+
*/
|
|
2738
|
+
hasActiveComponents() {
|
|
2739
|
+
return this.componentRegistry.toggles.size > 0 || this.componentRegistry.tabGroups.size > 0;
|
|
2740
|
+
}
|
|
2735
2741
|
getConfig() {
|
|
2736
2742
|
return this.config;
|
|
2737
2743
|
}
|
|
@@ -4190,97 +4196,7 @@ const WIDGET_STYLES = `
|
|
|
4190
4196
|
}
|
|
4191
4197
|
|
|
4192
4198
|
/* Dark theme custom state styles */
|
|
4193
|
-
/* Welcome modal styles */
|
|
4194
|
-
.cv-welcome-modal {
|
|
4195
|
-
max-width: 32rem;
|
|
4196
|
-
width: 90vw;
|
|
4197
|
-
background: white;
|
|
4198
|
-
border-radius: 0.75rem;
|
|
4199
|
-
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
4200
|
-
animation: slideIn 0.2s ease;
|
|
4201
|
-
display: flex;
|
|
4202
|
-
flex-direction: column;
|
|
4203
|
-
}
|
|
4204
|
-
|
|
4205
|
-
.cv-modal-main {
|
|
4206
|
-
padding: 1rem;
|
|
4207
|
-
flex: 1;
|
|
4208
|
-
display: flex;
|
|
4209
|
-
flex-direction: column;
|
|
4210
|
-
gap: 1rem;
|
|
4211
|
-
overflow-y: auto;
|
|
4212
|
-
max-height: calc(80vh - 8rem);
|
|
4213
|
-
}
|
|
4214
|
-
|
|
4215
|
-
.cv-welcome-message {
|
|
4216
|
-
font-size: 0.875rem;
|
|
4217
|
-
color: rgba(0, 0, 0, 0.8);
|
|
4218
|
-
margin: 0;
|
|
4219
|
-
line-height: 1.4;
|
|
4220
|
-
text-align: center;
|
|
4221
|
-
}
|
|
4222
|
-
|
|
4223
|
-
.cv-welcome-message a {
|
|
4224
|
-
color: #3e84f4;
|
|
4225
|
-
text-align: justify;
|
|
4226
|
-
text-decoration: none;
|
|
4227
|
-
}
|
|
4228
|
-
|
|
4229
|
-
.cv-welcome-message a:hover {
|
|
4230
|
-
text-decoration: underline;
|
|
4231
|
-
}
|
|
4232
|
-
|
|
4233
|
-
.cv-welcome-widget-preview {
|
|
4234
|
-
display: flex;
|
|
4235
|
-
align-items: center;
|
|
4236
|
-
justify-content: center;
|
|
4237
|
-
gap: 1rem;
|
|
4238
|
-
padding: 1rem;
|
|
4239
|
-
background: #f8f9fa;
|
|
4240
|
-
border-radius: 0.5rem;
|
|
4241
|
-
margin: 1rem 0;
|
|
4242
|
-
}
|
|
4243
4199
|
|
|
4244
|
-
.cv-welcome-widget-icon {
|
|
4245
|
-
width: 2rem;
|
|
4246
|
-
height: 2rem;
|
|
4247
|
-
background: rgba(62, 132, 244, 0.1);
|
|
4248
|
-
border-radius: 9999px;
|
|
4249
|
-
display: flex;
|
|
4250
|
-
align-items: center;
|
|
4251
|
-
justify-content: center;
|
|
4252
|
-
animation: cv-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
4253
|
-
color: #3e84f4;
|
|
4254
|
-
}
|
|
4255
|
-
|
|
4256
|
-
.cv-welcome-widget-label {
|
|
4257
|
-
font-size: 0.875rem;
|
|
4258
|
-
font-weight: 500;
|
|
4259
|
-
color: rgba(0, 0, 0, 0.8);
|
|
4260
|
-
margin: 0;
|
|
4261
|
-
}
|
|
4262
|
-
|
|
4263
|
-
.cv-welcome-got-it {
|
|
4264
|
-
width: 100%;
|
|
4265
|
-
background: #3e84f4;
|
|
4266
|
-
color: white;
|
|
4267
|
-
font-weight: 600;
|
|
4268
|
-
padding: 0.75rem 1rem;
|
|
4269
|
-
border-radius: 0.5rem;
|
|
4270
|
-
border: none;
|
|
4271
|
-
cursor: pointer;
|
|
4272
|
-
font-size: 0.875rem;
|
|
4273
|
-
transition: background-color 0.2s ease;
|
|
4274
|
-
outline: none;
|
|
4275
|
-
}
|
|
4276
|
-
|
|
4277
|
-
.cv-welcome-got-it:hover {
|
|
4278
|
-
background: rgba(62, 132, 244, 0.9);
|
|
4279
|
-
}
|
|
4280
|
-
|
|
4281
|
-
.cv-welcome-got-it:focus {
|
|
4282
|
-
box-shadow: 0 0 0 2px rgba(62, 132, 244, 0.5);
|
|
4283
|
-
}
|
|
4284
4200
|
|
|
4285
4201
|
/* Animations */
|
|
4286
4202
|
@keyframes cv-pulse {
|
|
@@ -4292,26 +4208,7 @@ const WIDGET_STYLES = `
|
|
|
4292
4208
|
}
|
|
4293
4209
|
}
|
|
4294
4210
|
|
|
4295
|
-
/* Dark theme welcome modal styles */
|
|
4296
|
-
.cv-widget-theme-dark .cv-welcome-modal {
|
|
4297
|
-
background: #101722;
|
|
4298
|
-
}
|
|
4299
4211
|
|
|
4300
|
-
.cv-widget-theme-dark .cv-welcome-message {
|
|
4301
|
-
color: rgba(255, 255, 255, 0.8);
|
|
4302
|
-
}
|
|
4303
|
-
|
|
4304
|
-
.cv-widget-theme-dark .cv-welcome-message a {
|
|
4305
|
-
color: #60a5fa;
|
|
4306
|
-
}
|
|
4307
|
-
|
|
4308
|
-
.cv-widget-theme-dark .cv-welcome-widget-preview {
|
|
4309
|
-
background: rgba(255, 255, 255, 0.1);
|
|
4310
|
-
}
|
|
4311
|
-
|
|
4312
|
-
.cv-widget-theme-dark .cv-welcome-widget-label {
|
|
4313
|
-
color: #e2e8f0;
|
|
4314
|
-
}
|
|
4315
4212
|
|
|
4316
4213
|
/* Dark theme logo box */
|
|
4317
4214
|
.cv-widget-theme-dark .cv-tabgroup-logo-box {
|
|
@@ -4486,6 +4383,176 @@ const WIDGET_STYLES = `
|
|
|
4486
4383
|
.cv-widget-theme-dark .cv-share-action-btn.primary:hover {
|
|
4487
4384
|
background: #2b74e6;
|
|
4488
4385
|
}
|
|
4386
|
+
|
|
4387
|
+
/* Intro Callout styles */
|
|
4388
|
+
.cv-widget-callout {
|
|
4389
|
+
position: fixed;
|
|
4390
|
+
background: white;
|
|
4391
|
+
border-radius: 8px;
|
|
4392
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
4393
|
+
padding: 12px 16px;
|
|
4394
|
+
width: 260px;
|
|
4395
|
+
z-index: 9999;
|
|
4396
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
4397
|
+
animation: cvFadeIn 0.3s ease-out;
|
|
4398
|
+
pointer-events: auto;
|
|
4399
|
+
display: flex;
|
|
4400
|
+
flex-direction: column;
|
|
4401
|
+
gap: 8px;
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4404
|
+
.cv-widget-callout-text {
|
|
4405
|
+
font-size: 0.9rem;
|
|
4406
|
+
color: #333;
|
|
4407
|
+
margin: 0;
|
|
4408
|
+
line-height: 1.4;
|
|
4409
|
+
}
|
|
4410
|
+
|
|
4411
|
+
.cv-widget-callout-close {
|
|
4412
|
+
position: absolute;
|
|
4413
|
+
top: 6px;
|
|
4414
|
+
right: 6px;
|
|
4415
|
+
width: 18px;
|
|
4416
|
+
height: 18px;
|
|
4417
|
+
border: none;
|
|
4418
|
+
background: rgba(0,0,0,0.05);
|
|
4419
|
+
color: #666;
|
|
4420
|
+
cursor: pointer;
|
|
4421
|
+
display: flex;
|
|
4422
|
+
align-items: center;
|
|
4423
|
+
justify-content: center;
|
|
4424
|
+
border-radius: 50%;
|
|
4425
|
+
font-size: 14px;
|
|
4426
|
+
line-height: 1;
|
|
4427
|
+
padding: 0;
|
|
4428
|
+
transition: all 0.2s ease;
|
|
4429
|
+
}
|
|
4430
|
+
|
|
4431
|
+
.cv-widget-callout-close:hover {
|
|
4432
|
+
background: #f0f0f0;
|
|
4433
|
+
color: #333;
|
|
4434
|
+
}
|
|
4435
|
+
|
|
4436
|
+
/* Callout positioning and arrow */
|
|
4437
|
+
.cv-widget-callout::after {
|
|
4438
|
+
content: '';
|
|
4439
|
+
position: absolute;
|
|
4440
|
+
width: 10px;
|
|
4441
|
+
height: 10px;
|
|
4442
|
+
background: white;
|
|
4443
|
+
transform: rotate(45deg);
|
|
4444
|
+
box-shadow: 1px 1px 1px rgba(0,0,0,0.05); /* subtle shadow for arrow */
|
|
4445
|
+
}
|
|
4446
|
+
|
|
4447
|
+
/* Top-Right Widget -> Callout to the left */
|
|
4448
|
+
.cv-widget-callout.cv-pos-top-right {
|
|
4449
|
+
top: 20px;
|
|
4450
|
+
right: 64px;
|
|
4451
|
+
}
|
|
4452
|
+
.cv-widget-callout.cv-pos-top-right::after {
|
|
4453
|
+
top: 13px;
|
|
4454
|
+
right: -5px;
|
|
4455
|
+
box-shadow: 1px -1px 1px rgba(0,0,0,0.05);
|
|
4456
|
+
transform: rotate(45deg);
|
|
4457
|
+
}
|
|
4458
|
+
|
|
4459
|
+
/* Bottom-Right Widget -> Callout to the left */
|
|
4460
|
+
.cv-widget-callout.cv-pos-bottom-right {
|
|
4461
|
+
bottom: 20px;
|
|
4462
|
+
right: 64px;
|
|
4463
|
+
}
|
|
4464
|
+
.cv-widget-callout.cv-pos-bottom-right::after {
|
|
4465
|
+
bottom: 13px;
|
|
4466
|
+
right: -5px;
|
|
4467
|
+
}
|
|
4468
|
+
|
|
4469
|
+
/* Top-Left Widget -> Callout to the right */
|
|
4470
|
+
.cv-widget-callout.cv-pos-top-left {
|
|
4471
|
+
top: 20px;
|
|
4472
|
+
left: 64px;
|
|
4473
|
+
}
|
|
4474
|
+
.cv-widget-callout.cv-pos-top-left::after {
|
|
4475
|
+
top: 13px;
|
|
4476
|
+
left: -5px;
|
|
4477
|
+
}
|
|
4478
|
+
|
|
4479
|
+
/* Bottom-Left Widget -> Callout to the right */
|
|
4480
|
+
.cv-widget-callout.cv-pos-bottom-left {
|
|
4481
|
+
bottom: 20px;
|
|
4482
|
+
left: 64px;
|
|
4483
|
+
}
|
|
4484
|
+
.cv-widget-callout.cv-pos-bottom-left::after {
|
|
4485
|
+
bottom: 13px;
|
|
4486
|
+
left: -5px;
|
|
4487
|
+
}
|
|
4488
|
+
|
|
4489
|
+
/* Middle-Right Widget -> Callout to the left */
|
|
4490
|
+
.cv-widget-callout.cv-pos-middle-right {
|
|
4491
|
+
top: 50%;
|
|
4492
|
+
right: 64px;
|
|
4493
|
+
transform: translateY(-50%);
|
|
4494
|
+
}
|
|
4495
|
+
.cv-widget-callout.cv-pos-middle-right::after {
|
|
4496
|
+
top: 50%;
|
|
4497
|
+
right: -5px;
|
|
4498
|
+
transform: translateY(-50%) rotate(45deg);
|
|
4499
|
+
}
|
|
4500
|
+
|
|
4501
|
+
/* Middle-Left Widget -> Callout to the right */
|
|
4502
|
+
.cv-widget-callout.cv-pos-middle-left {
|
|
4503
|
+
top: 50%;
|
|
4504
|
+
left: 64px;
|
|
4505
|
+
transform: translateY(-50%);
|
|
4506
|
+
}
|
|
4507
|
+
.cv-widget-callout.cv-pos-middle-left::after {
|
|
4508
|
+
top: 50%;
|
|
4509
|
+
left: -5px;
|
|
4510
|
+
transform: translateY(-50%) rotate(45deg);
|
|
4511
|
+
}
|
|
4512
|
+
|
|
4513
|
+
/* Pulse animation utility */
|
|
4514
|
+
.cv-widget-icon.cv-pulse {
|
|
4515
|
+
animation: cv-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
4516
|
+
box-shadow: 0 0 0 0 rgba(62, 132, 244, 0.7);
|
|
4517
|
+
}
|
|
4518
|
+
|
|
4519
|
+
@keyframes cv-pulse {
|
|
4520
|
+
0% {
|
|
4521
|
+
transform: scale(1);
|
|
4522
|
+
box-shadow: 0 0 0 0 rgba(62, 132, 244, 0.7);
|
|
4523
|
+
}
|
|
4524
|
+
70% {
|
|
4525
|
+
transform: scale(1.05);
|
|
4526
|
+
box-shadow: 0 0 0 10px rgba(62, 132, 244, 0);
|
|
4527
|
+
}
|
|
4528
|
+
100% {
|
|
4529
|
+
transform: scale(1);
|
|
4530
|
+
box-shadow: 0 0 0 0 rgba(62, 132, 244, 0);
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
|
|
4534
|
+
/* Dark Theme */
|
|
4535
|
+
.cv-widget-theme-dark .cv-widget-callout {
|
|
4536
|
+
background: #1f2937; /* Tailwind gray-800 mostly */
|
|
4537
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
|
4538
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
4539
|
+
}
|
|
4540
|
+
.cv-widget-theme-dark .cv-widget-callout::after {
|
|
4541
|
+
background: #1f2937;
|
|
4542
|
+
border-top: 1px solid rgba(255,255,255,0.1);
|
|
4543
|
+
border-right: 1px solid rgba(255,255,255,0.1);
|
|
4544
|
+
}
|
|
4545
|
+
.cv-widget-theme-dark .cv-widget-callout-text {
|
|
4546
|
+
color: #e5e7eb;
|
|
4547
|
+
}
|
|
4548
|
+
.cv-widget-theme-dark .cv-widget-callout-close {
|
|
4549
|
+
background: rgba(255,255,255,0.1);
|
|
4550
|
+
color: #9ca3af;
|
|
4551
|
+
}
|
|
4552
|
+
.cv-widget-theme-dark .cv-widget-callout-close:hover {
|
|
4553
|
+
background: rgba(255,255,255,0.2);
|
|
4554
|
+
color: #fff;
|
|
4555
|
+
}
|
|
4489
4556
|
`;
|
|
4490
4557
|
/**
|
|
4491
4558
|
* Inject widget styles into the document head
|
|
@@ -4504,6 +4571,7 @@ class CustomViewsWidget {
|
|
|
4504
4571
|
core;
|
|
4505
4572
|
container;
|
|
4506
4573
|
widgetIcon = null;
|
|
4574
|
+
introCallout = null;
|
|
4507
4575
|
options;
|
|
4508
4576
|
_hasVisibleConfig = false;
|
|
4509
4577
|
pageToggleIds = new Set();
|
|
@@ -4524,8 +4592,7 @@ class CustomViewsWidget {
|
|
|
4524
4592
|
title: options.title || 'Customize View',
|
|
4525
4593
|
description: options.description || '',
|
|
4526
4594
|
showWelcome: options.showWelcome ?? false,
|
|
4527
|
-
|
|
4528
|
-
welcomeMessage: options.welcomeMessage || 'This site is powered by Custom Views. Use the widget on the side (⚙) to customize your experience. Your preferences will be saved and can be shared via URL.<br><br>Learn more at <a href="https://github.com/customviews-js/customviews" target="_blank">customviews GitHub</a>.',
|
|
4595
|
+
welcomeMessage: options.welcomeMessage || 'Customize your reading experience (theme, toggles, tabs) here.',
|
|
4529
4596
|
showTabGroups: options.showTabGroups ?? true
|
|
4530
4597
|
};
|
|
4531
4598
|
// Determine if there are any configurations to show
|
|
@@ -4567,9 +4634,9 @@ class CustomViewsWidget {
|
|
|
4567
4634
|
this.attachEventListeners();
|
|
4568
4635
|
// Always append to body since it's a floating icon
|
|
4569
4636
|
document.body.appendChild(this.widgetIcon);
|
|
4570
|
-
// Show
|
|
4637
|
+
// Show intro callout on first visit if enabled
|
|
4571
4638
|
if (this.options.showWelcome) {
|
|
4572
|
-
this.
|
|
4639
|
+
this.showIntroCalloutIfFirstVisit();
|
|
4573
4640
|
}
|
|
4574
4641
|
return this.widgetIcon;
|
|
4575
4642
|
}
|
|
@@ -4599,6 +4666,11 @@ class CustomViewsWidget {
|
|
|
4599
4666
|
this.stateModal.remove();
|
|
4600
4667
|
this.stateModal = null;
|
|
4601
4668
|
}
|
|
4669
|
+
// Clean up callout
|
|
4670
|
+
if (this.introCallout) {
|
|
4671
|
+
this.introCallout.remove();
|
|
4672
|
+
this.introCallout = null;
|
|
4673
|
+
}
|
|
4602
4674
|
}
|
|
4603
4675
|
attachEventListeners() {
|
|
4604
4676
|
if (!this.widgetIcon)
|
|
@@ -4614,10 +4686,48 @@ class CustomViewsWidget {
|
|
|
4614
4686
|
this.stateModal.classList.add('cv-hidden');
|
|
4615
4687
|
}
|
|
4616
4688
|
}
|
|
4689
|
+
/**
|
|
4690
|
+
* Dismiss the intro callout
|
|
4691
|
+
*/
|
|
4692
|
+
dismissIntroCallout() {
|
|
4693
|
+
if (!this.introCallout)
|
|
4694
|
+
return;
|
|
4695
|
+
const callout = this.introCallout;
|
|
4696
|
+
// Clear reference immediately from class to prevent re-use
|
|
4697
|
+
this.introCallout = null;
|
|
4698
|
+
callout.remove();
|
|
4699
|
+
// Stop pulsing the widget icon
|
|
4700
|
+
if (this.widgetIcon) {
|
|
4701
|
+
this.widgetIcon.classList.remove('cv-pulse');
|
|
4702
|
+
}
|
|
4703
|
+
// Mark as shown in localStorage
|
|
4704
|
+
try {
|
|
4705
|
+
localStorage.setItem('cv-intro-shown', 'true');
|
|
4706
|
+
}
|
|
4707
|
+
catch (e) {
|
|
4708
|
+
// Ignore localStorage errors
|
|
4709
|
+
}
|
|
4710
|
+
}
|
|
4617
4711
|
/**
|
|
4618
4712
|
* Open the custom state creator
|
|
4619
4713
|
*/
|
|
4620
4714
|
openStateModal() {
|
|
4715
|
+
// Dismiss intro callout if valid
|
|
4716
|
+
if (this.introCallout) {
|
|
4717
|
+
this.dismissIntroCallout();
|
|
4718
|
+
}
|
|
4719
|
+
else {
|
|
4720
|
+
// Even if no callout is shown (e.g. page had no content), opening the widget
|
|
4721
|
+
// should count as "seen", preventing future callouts.
|
|
4722
|
+
try {
|
|
4723
|
+
if (!localStorage.getItem('cv-intro-shown')) {
|
|
4724
|
+
localStorage.setItem('cv-intro-shown', 'true');
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
catch (e) {
|
|
4728
|
+
// Ignore localStorage errors
|
|
4729
|
+
}
|
|
4730
|
+
}
|
|
4621
4731
|
if (!this.stateModal) {
|
|
4622
4732
|
this._createStateModal();
|
|
4623
4733
|
}
|
|
@@ -5075,88 +5185,70 @@ class CustomViewsWidget {
|
|
|
5075
5185
|
}
|
|
5076
5186
|
}
|
|
5077
5187
|
/**
|
|
5078
|
-
* Check if this is the first visit and show
|
|
5188
|
+
* Check if this is the first visit and show intro callout
|
|
5079
5189
|
*/
|
|
5080
|
-
|
|
5190
|
+
showIntroCalloutIfFirstVisit() {
|
|
5081
5191
|
if (!this._hasVisibleConfig)
|
|
5082
5192
|
return;
|
|
5083
|
-
|
|
5084
|
-
//
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5193
|
+
// Strict check: Only show callout if there is actual content on the page to customize.
|
|
5194
|
+
// We check the core registry for any active toggles or tab groups.
|
|
5195
|
+
if (!this.core.hasActiveComponents()) {
|
|
5196
|
+
return;
|
|
5197
|
+
}
|
|
5198
|
+
const STORAGE_KEY = 'cv-intro-shown';
|
|
5199
|
+
// Check if intro has been shown before
|
|
5200
|
+
let hasSeenIntro = null;
|
|
5201
|
+
try {
|
|
5202
|
+
hasSeenIntro = localStorage.getItem(STORAGE_KEY);
|
|
5203
|
+
}
|
|
5204
|
+
catch (e) {
|
|
5205
|
+
// Ignore localStorage errors (e.g. private mode)
|
|
5206
|
+
}
|
|
5207
|
+
if (!hasSeenIntro) {
|
|
5208
|
+
// Show callout after a short delay
|
|
5088
5209
|
setTimeout(() => {
|
|
5089
|
-
this.
|
|
5090
|
-
},
|
|
5091
|
-
// Mark as shown
|
|
5092
|
-
localStorage.setItem(STORAGE_KEY, 'true');
|
|
5210
|
+
this.createCallout();
|
|
5211
|
+
}, 1000);
|
|
5093
5212
|
}
|
|
5094
5213
|
}
|
|
5095
5214
|
/**
|
|
5096
|
-
* Create and show the
|
|
5215
|
+
* Create and show the intro callout
|
|
5097
5216
|
*/
|
|
5098
|
-
|
|
5099
|
-
//
|
|
5100
|
-
if (this.
|
|
5217
|
+
createCallout() {
|
|
5218
|
+
// Avoid duplicates
|
|
5219
|
+
if (this.introCallout || document.querySelector('.cv-widget-callout'))
|
|
5101
5220
|
return;
|
|
5102
|
-
|
|
5103
|
-
|
|
5221
|
+
this.introCallout = document.createElement('div');
|
|
5222
|
+
const callout = this.introCallout;
|
|
5223
|
+
callout.className = `cv-widget-callout cv-pos-${this.options.position}`;
|
|
5104
5224
|
if (this.options.theme === 'dark') {
|
|
5105
|
-
|
|
5225
|
+
callout.classList.add('cv-widget-theme-dark');
|
|
5226
|
+
}
|
|
5227
|
+
// Close button
|
|
5228
|
+
const closeBtn = document.createElement('button');
|
|
5229
|
+
closeBtn.className = 'cv-widget-callout-close';
|
|
5230
|
+
closeBtn.innerHTML = '×';
|
|
5231
|
+
closeBtn.setAttribute('aria-label', 'Dismiss intro');
|
|
5232
|
+
closeBtn.addEventListener('click', (e) => {
|
|
5233
|
+
e.stopPropagation();
|
|
5234
|
+
this.dismissIntroCallout();
|
|
5235
|
+
});
|
|
5236
|
+
// Message
|
|
5237
|
+
const msg = document.createElement('p');
|
|
5238
|
+
msg.className = 'cv-widget-callout-text';
|
|
5239
|
+
msg.textContent = this.options.welcomeMessage;
|
|
5240
|
+
callout.appendChild(closeBtn);
|
|
5241
|
+
callout.appendChild(msg);
|
|
5242
|
+
document.body.appendChild(callout);
|
|
5243
|
+
// Add pulse to widget icon to draw attention
|
|
5244
|
+
if (this.widgetIcon) {
|
|
5245
|
+
this.widgetIcon.classList.add('cv-pulse');
|
|
5106
5246
|
}
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
<div class="cv-modal-icon">
|
|
5112
|
-
${getGearIcon()}
|
|
5113
|
-
</div>
|
|
5114
|
-
<h1 class="cv-modal-title">${this.options.welcomeTitle}</h1>
|
|
5115
|
-
</div>
|
|
5116
|
-
</header>
|
|
5117
|
-
<div class="cv-modal-main">
|
|
5118
|
-
<p class="cv-welcome-message">${this.options.welcomeMessage}</p>
|
|
5119
|
-
|
|
5120
|
-
<div class="cv-welcome-widget-preview">
|
|
5121
|
-
<div class="cv-welcome-widget-icon">
|
|
5122
|
-
${getGearIcon()}
|
|
5123
|
-
</div>
|
|
5124
|
-
<p class="cv-welcome-widget-label">Look for this widget</p>
|
|
5125
|
-
</div>
|
|
5126
|
-
|
|
5127
|
-
<button class="cv-welcome-got-it">Got it!</button>
|
|
5128
|
-
</div>
|
|
5129
|
-
</div>
|
|
5130
|
-
`;
|
|
5131
|
-
document.body.appendChild(welcomeModal);
|
|
5132
|
-
this.attachWelcomeModalEventListeners(welcomeModal);
|
|
5133
|
-
}
|
|
5134
|
-
/**
|
|
5135
|
-
* Attach event listeners for welcome modal
|
|
5136
|
-
*/
|
|
5137
|
-
attachWelcomeModalEventListeners(welcomeModal) {
|
|
5138
|
-
const closeModal = () => {
|
|
5139
|
-
welcomeModal.remove();
|
|
5140
|
-
document.removeEventListener('keydown', handleEscape);
|
|
5141
|
-
};
|
|
5142
|
-
// Got it button
|
|
5143
|
-
const gotItBtn = welcomeModal.querySelector('.cv-welcome-got-it');
|
|
5144
|
-
if (gotItBtn) {
|
|
5145
|
-
gotItBtn.addEventListener('click', closeModal);
|
|
5146
|
-
}
|
|
5147
|
-
// Overlay click to close
|
|
5148
|
-
welcomeModal.addEventListener('click', (e) => {
|
|
5149
|
-
if (e.target === welcomeModal) {
|
|
5150
|
-
closeModal();
|
|
5151
|
-
}
|
|
5247
|
+
// Auto-dismiss and open widget on click anywhere on callout
|
|
5248
|
+
callout.addEventListener('click', () => {
|
|
5249
|
+
this.dismissIntroCallout();
|
|
5250
|
+
this.openStateModal();
|
|
5152
5251
|
});
|
|
5153
|
-
// Escape key to close
|
|
5154
|
-
const handleEscape = (e) => {
|
|
5155
|
-
if (e.key === 'Escape') {
|
|
5156
|
-
closeModal();
|
|
5157
|
-
}
|
|
5158
|
-
};
|
|
5159
|
-
document.addEventListener('keydown', handleEscape);
|
|
5160
5252
|
}
|
|
5161
5253
|
}
|
|
5162
5254
|
|