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