@kajidog/connpass-mcp-server 0.2.0 → 0.4.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.
Files changed (59) hide show
  1. package/README.md +72 -149
  2. package/dist/index.d.ts +0 -2
  3. package/dist/index.js +4729 -162
  4. package/dist/index.js.map +1 -1
  5. package/dist/mcp-app.html +139 -0
  6. package/dist/stdio.d.ts +1 -0
  7. package/dist/stdio.js +2120 -0
  8. package/dist/stdio.js.map +1 -0
  9. package/package.json +21 -20
  10. package/dist/apps-sdk.d.ts +0 -8
  11. package/dist/apps-sdk.d.ts.map +0 -1
  12. package/dist/apps-sdk.js +0 -79
  13. package/dist/apps-sdk.js.map +0 -1
  14. package/dist/config.d.ts +0 -7
  15. package/dist/config.d.ts.map +0 -1
  16. package/dist/config.js +0 -82
  17. package/dist/config.js.map +0 -1
  18. package/dist/index.d.ts.map +0 -1
  19. package/dist/tools/events.d.ts +0 -82
  20. package/dist/tools/events.d.ts.map +0 -1
  21. package/dist/tools/events.js +0 -385
  22. package/dist/tools/events.js.map +0 -1
  23. package/dist/tools/formatting.d.ts +0 -74
  24. package/dist/tools/formatting.d.ts.map +0 -1
  25. package/dist/tools/formatting.js +0 -211
  26. package/dist/tools/formatting.js.map +0 -1
  27. package/dist/tools/groups.d.ts +0 -48
  28. package/dist/tools/groups.d.ts.map +0 -1
  29. package/dist/tools/groups.js +0 -106
  30. package/dist/tools/groups.js.map +0 -1
  31. package/dist/tools/index.d.ts +0 -58
  32. package/dist/tools/index.d.ts.map +0 -1
  33. package/dist/tools/index.js +0 -21
  34. package/dist/tools/index.js.map +0 -1
  35. package/dist/tools/shared.d.ts +0 -24
  36. package/dist/tools/shared.d.ts.map +0 -1
  37. package/dist/tools/shared.js +0 -113
  38. package/dist/tools/shared.js.map +0 -1
  39. package/dist/tools/users.d.ts +0 -51
  40. package/dist/tools/users.d.ts.map +0 -1
  41. package/dist/tools/users.js +0 -239
  42. package/dist/tools/users.js.map +0 -1
  43. package/dist/transports/http.d.ts +0 -17
  44. package/dist/transports/http.d.ts.map +0 -1
  45. package/dist/transports/http.js +0 -185
  46. package/dist/transports/http.js.map +0 -1
  47. package/dist/transports/sse.d.ts +0 -21
  48. package/dist/transports/sse.d.ts.map +0 -1
  49. package/dist/transports/sse.js +0 -161
  50. package/dist/transports/sse.js.map +0 -1
  51. package/dist/widgets/connpass-events.d.ts +0 -29
  52. package/dist/widgets/connpass-events.d.ts.map +0 -1
  53. package/dist/widgets/connpass-events.html +0 -2269
  54. package/dist/widgets/connpass-events.js +0 -58
  55. package/dist/widgets/connpass-events.js.map +0 -1
  56. package/dist/widgets/index.d.ts +0 -6
  57. package/dist/widgets/index.d.ts.map +0 -1
  58. package/dist/widgets/index.js +0 -34
  59. package/dist/widgets/index.js.map +0 -1
@@ -1,2269 +0,0 @@
1
- <!doctype html>
2
- <html lang="ja">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
6
- <title>Connpass Events</title>
7
- <style>
8
- :root {
9
- color-scheme: light;
10
- font-family: "Inter", "Noto Sans JP", system-ui, -apple-system, BlinkMacSystemFont,
11
- "Segoe UI", sans-serif;
12
- --connpass-surface: #0d0d0d0d;;
13
- --connpass-card-bg: #fff;
14
- --connpass-card-border: rgba(17, 17, 17, 0.08);
15
- --connpass-card-shadow: rgba(17, 17, 17, 0.06);
16
- --connpass-text-primary: rgba(17, 17, 17, 0.92);
17
- --connpass-text-secondary: rgba(17, 17, 17, 0.6);
18
- --connpass-text-muted: rgba(17, 17, 17, 0.45);
19
- --connpass-button-bg: #111;
20
- --connpass-button-bg-hover: rgba(17, 17, 17, 0.88);
21
- --connpass-button-text: #fff;
22
- --connpass-accent: #2563eb;
23
- --connpass-badge-bg: rgba(255, 173, 51, 0.18);
24
- --connpass-badge-text: #a35400;
25
- background-color: transparent;
26
- color: var(--connpass-text-primary);
27
- }
28
-
29
- @media (prefers-color-scheme: dark) {
30
- :root {
31
- color-scheme: dark;
32
- --connpass-surface: #333;
33
- --connpass-card-bg: rgba(48, 49, 55, 0.92);
34
- --connpass-card-border: rgba(255, 255, 255, 0.08);
35
- --connpass-card-shadow: rgba(0, 0, 0, 0.45);
36
- --connpass-text-primary: rgba(255, 255, 255, 0.92);
37
- --connpass-text-secondary: rgba(255, 255, 255, 0.7);
38
- --connpass-text-muted: rgba(255, 255, 255, 0.5);
39
- --connpass-button-bg: rgba(255, 255, 255, 0.12);
40
- --connpass-button-bg-hover: rgba(255, 255, 255, 0.2);
41
- --connpass-button-text: rgba(255, 255, 255, 0.96);
42
- --connpass-accent: #60a5fa;
43
- --connpass-badge-bg: rgba(251, 191, 36, 0.2);
44
- --connpass-badge-text: rgba(251, 191, 36, 0.92);
45
- }
46
- }
47
-
48
- body {
49
- margin: 0;
50
- background: transparent;
51
- font-size: 15px;
52
- line-height: 1.6;
53
- color: var(--connpass-text-primary);
54
- }
55
-
56
- *,
57
- *::before,
58
- *::after {
59
- box-sizing: border-box;
60
- }
61
-
62
- button {
63
- font: inherit;
64
- }
65
-
66
- .connpass-empty {
67
- display: flex;
68
- flex-direction: column;
69
- align-items: center;
70
- justify-content: center;
71
- gap: 12px;
72
- min-height: 200px;
73
- border: 1px dashed var(--connpass-card-border);
74
- border-radius: 18px;
75
- padding: 16px 24px;
76
- background: var(--connpass-surface);
77
- color: var(--connpass-text-secondary);
78
- text-align: center;
79
- }
80
-
81
- .connpass-carousel {
82
- position: relative;
83
- width: 100%;
84
- padding: 18px 16px;
85
- background: var(--connpass-surface);
86
- box-shadow: 0 12px 30px var(--connpass-card-shadow);
87
- }
88
-
89
- .connpass-carousel__scroller {
90
- display: flex;
91
- gap: 16px;
92
- overflow-x: auto;
93
- overscroll-behavior-x: contain;
94
- scroll-snap-type: x mandatory;
95
- padding-bottom: 6px;
96
- background: var(--connpass-surface);
97
- }
98
-
99
- .connpass-carousel__scroller::-webkit-scrollbar {
100
- display: none;
101
- }
102
-
103
- .connpass-card {
104
- position: relative;
105
- flex: 0 0 280px;
106
- width: 280px;
107
- display: flex;
108
- flex-direction: column;
109
- justify-content: flex-start;
110
- gap: 18px;
111
- min-height: 260px;
112
- background: var(--connpass-card-bg);
113
- border: 1px solid var(--connpass-card-border);
114
- border-radius: 20px;
115
- box-shadow: 0 18px 35px var(--connpass-card-shadow);
116
- padding: 18px;
117
- scroll-snap-align: start;
118
- transition: transform 160ms ease, box-shadow 160ms ease;
119
- }
120
-
121
- .connpass-card__content {
122
- display: flex;
123
- flex-direction: column;
124
- gap: 12px;
125
- align-items: flex-start;
126
- width: 100%;
127
- }
128
-
129
- .connpass-card__media {
130
- position: relative;
131
- overflow: hidden;
132
- border-radius: 16px;
133
- aspect-ratio: 16 / 9;
134
- margin: -18px -18px 0px;
135
- background: color-mix(in srgb, var(--connpass-accent) 14%, transparent);
136
- max-height: 200px;
137
- }
138
-
139
- .connpass-card__media img {
140
- width: 100%;
141
- height: 100%;
142
- object-fit: cover;
143
- display: block;
144
- }
145
-
146
- .connpass-card__media--fallback {
147
- display: flex;
148
- align-items: center;
149
- justify-content: center;
150
- color: var(--connpass-text-secondary);
151
- font-weight: 600;
152
- letter-spacing: 0.02em;
153
- text-transform: none;
154
- font-size: 13px;
155
- padding: 0 12px;
156
- text-align: center;
157
- }
158
-
159
- .connpass-card__media-fallback-label {
160
- display: block;
161
- overflow: hidden;
162
- text-overflow: ellipsis;
163
- white-space: nowrap;
164
- width: 100%;
165
- }
166
-
167
- .connpass-hero {
168
- position: relative;
169
- width: 100%;
170
- aspect-ratio: 16 / 9;
171
- background: color-mix(in srgb, var(--connpass-accent) 18%, transparent);
172
- max-height: min(40vh, 320px);
173
- border-bottom: 1px solid var(--connpass-card-border);
174
- }
175
-
176
- .connpass-hero img {
177
- width: 100%;
178
- height: 100%;
179
- object-fit: cover;
180
- display: block;
181
- }
182
-
183
- .connpass-hero--fallback {
184
- display: flex;
185
- align-items: center;
186
- justify-content: center;
187
- color: var(--connpass-text-secondary);
188
- font-size: 18px;
189
- font-weight: 650;
190
- letter-spacing: 0.02em;
191
- text-transform: none;
192
- padding: 0 24px;
193
- text-align: center;
194
- }
195
-
196
- .connpass-hero__label {
197
- display: block;
198
- max-width: 90%;
199
- overflow: hidden;
200
- text-overflow: ellipsis;
201
- white-space: nowrap;
202
- }
203
-
204
- .connpass-hero__badge {
205
- position: absolute;
206
- bottom: 12px;
207
- left: 16px;
208
- display: inline-flex;
209
- align-items: center;
210
- gap: 6px;
211
- padding: 6px 12px;
212
- border-radius: 999px;
213
- background: color-mix(in srgb, var(--connpass-card-bg) 82%, transparent);
214
- color: var(--connpass-text-primary);
215
- font-size: 12px;
216
- font-weight: 600;
217
- box-shadow: 0 10px 18px var(--connpass-card-shadow);
218
- }
219
-
220
- .connpass-card__content .connpass-badge {
221
- margin-top: 12px;
222
- align-self: flex-start;
223
- }
224
-
225
- .connpass-pill-button--ghost {
226
- background: var(--connpass-card-bg);
227
- color: var(--connpass-text-secondary);
228
- border-color: var(--connpass-card-border);
229
- }
230
-
231
- .connpass-pill-button--ghost:hover {
232
- background: color-mix(in srgb, var(--connpass-card-bg) 80%, transparent);
233
- }
234
-
235
- .connpass-card:hover {
236
- transform: translateY(-4px);
237
- box-shadow: 0 22px 40px var(--connpass-card-shadow);
238
- }
239
-
240
- .connpass-agenda {
241
- display: flex;
242
- flex-direction: column;
243
- gap: 24px;
244
- padding: 18px 16px 24px;
245
- background: var(--connpass-surface);
246
- }
247
-
248
- .connpass-agenda__chips {
249
- display: flex;
250
- flex-wrap: wrap;
251
- gap: 8px;
252
- }
253
-
254
- .connpass-chip {
255
- display: inline-flex;
256
- align-items: center;
257
- gap: 6px;
258
- padding: 6px 12px;
259
- border-radius: 999px;
260
- background: color-mix(in srgb, var(--connpass-accent) 18%, transparent);
261
- color: var(--connpass-accent);
262
- font-size: 12px;
263
- font-weight: 600;
264
- letter-spacing: 0.01em;
265
- }
266
-
267
- .connpass-agenda__section {
268
- display: flex;
269
- flex-direction: column;
270
- gap: 12px;
271
- }
272
-
273
- .connpass-agenda__section-header {
274
- display: flex;
275
- flex-direction: column;
276
- gap: 4px;
277
- }
278
-
279
- .connpass-agenda__section-title {
280
- margin: 0;
281
- font-size: 15px;
282
- font-weight: 700;
283
- color: var(--connpass-text-primary);
284
- }
285
-
286
- .connpass-agenda__section-subtitle {
287
- margin: 0;
288
- font-size: 13px;
289
- color: var(--connpass-text-secondary);
290
- }
291
-
292
- .connpass-agenda__list {
293
- display: flex;
294
- flex-direction: column;
295
- gap: 12px;
296
- }
297
-
298
- .connpass-agenda__empty {
299
- border-radius: 16px;
300
- border: 1px dashed var(--connpass-card-border);
301
- padding: 20px;
302
- text-align: center;
303
- font-size: 13px;
304
- color: var(--connpass-text-secondary);
305
- background: color-mix(in srgb, var(--connpass-card-bg) 65%, transparent);
306
- }
307
-
308
- .connpass-agenda__meta {
309
- display: flex;
310
- flex-wrap: wrap;
311
- gap: 10px;
312
- font-size: 12px;
313
- color: var(--connpass-text-muted);
314
- padding-top: 8px;
315
- }
316
-
317
- .connpass-list-card {
318
- display: flex;
319
- flex-direction: column;
320
- gap: 14px;
321
- border-radius: 18px;
322
- border: 1px solid var(--connpass-card-border);
323
- background: var(--connpass-card-bg);
324
- box-shadow: 0 18px 35px var(--connpass-card-shadow);
325
- padding: 16px;
326
- }
327
-
328
- .connpass-list-card__header {
329
- display: flex;
330
- align-items: center;
331
- gap: 12px;
332
- padding-bottom: 12px;
333
- border-bottom: 1px solid var(--connpass-card-border);
334
- }
335
-
336
- .connpass-list-card__header-media {
337
- flex: 0 0 48px;
338
- width: 48px;
339
- height: 48px;
340
- border-radius: 50%;
341
- overflow: hidden;
342
- background: color-mix(in srgb, var(--connpass-accent) 14%, transparent);
343
- display: flex;
344
- align-items: center;
345
- justify-content: center;
346
- }
347
-
348
- .connpass-list-card__header-media img {
349
- width: 100%;
350
- height: 100%;
351
- object-fit: cover;
352
- }
353
-
354
- .connpass-list-card__media-fallback {
355
- font-size: 10px;
356
- font-weight: 600;
357
- color: var(--connpass-text-secondary);
358
- text-align: center;
359
- padding: 0 4px;
360
- }
361
-
362
- .connpass-list-card__header-text {
363
- flex: 1 1 auto;
364
- display: flex;
365
- flex-direction: column;
366
- gap: 4px;
367
- min-width: 0;
368
- }
369
-
370
- .connpass-list-card__title {
371
- margin: 0;
372
- font-size: 16px;
373
- font-weight: 650;
374
- color: var(--connpass-text-primary);
375
- line-height: 1.3;
376
- overflow: hidden;
377
- text-overflow: ellipsis;
378
- white-space: nowrap;
379
- }
380
-
381
- .connpass-list-card__owner {
382
- font-size: 12px;
383
- color: var(--connpass-text-secondary);
384
- overflow: hidden;
385
- text-overflow: ellipsis;
386
- white-space: nowrap;
387
- }
388
-
389
- .connpass-list-card__body {
390
- flex: 1 1 auto;
391
- display: flex;
392
- flex-direction: column;
393
- gap: 10px;
394
- min-width: 0;
395
- }
396
-
397
- .connpass-list-card__catchphrase {
398
- margin: 0;
399
- font-size: 13px;
400
- color: var(--connpass-text-secondary);
401
- line-height: 1.5;
402
- display: -webkit-box;
403
- -webkit-line-clamp: 2;
404
- -webkit-box-orient: vertical;
405
- overflow: hidden;
406
- }
407
-
408
- .connpass-list-card__meta {
409
- display: flex;
410
- flex-direction: column;
411
- gap: 6px;
412
- }
413
-
414
- .connpass-list-card__meta .connpass-row {
415
- margin: 0;
416
- }
417
-
418
- .connpass-list-card__actions {
419
- display: flex;
420
- flex-wrap: wrap;
421
- gap: 8px;
422
- margin-top: auto;
423
- }
424
-
425
- .connpass-card__title {
426
- margin: 0;
427
- font-size: 16px;
428
- font-weight: 650;
429
- line-height: 1.3;
430
- width: 100%;
431
- }
432
-
433
- .connpass-badge {
434
- display: inline-flex;
435
- align-items: center;
436
- gap: 6px;
437
- padding: 6px 10px;
438
- border-radius: 999px;
439
- background: var(--connpass-badge-bg);
440
- color: var(--connpass-badge-text);
441
- font-size: 12px;
442
- font-weight: 600;
443
- line-height: 1;
444
- letter-spacing: 0.01em;
445
- white-space: nowrap;
446
- font-variant-numeric: tabular-nums;
447
- }
448
-
449
- .connpass-row {
450
- display: flex;
451
- align-items: flex-start;
452
- gap: 9px;
453
- color: var(--connpass-text-secondary);
454
- font-size: 13px;
455
- }
456
-
457
- .connpass-card__content .connpass-row {
458
- margin: 0;
459
- }
460
-
461
- .connpass-card__content .connpass-badge,
462
- .connpass-card__content .connpass-tag,
463
- .connpass-card__content .connpass-card__catchphrase {
464
- margin: 0;
465
- }
466
-
467
- .connpass-row__icon {
468
- width: 16px;
469
- height: 16px;
470
- margin-top: 2px;
471
- color: var(--connpass-text-muted);
472
- }
473
-
474
- .connpass-card__footer {
475
- display: flex;
476
- align-items: center;
477
- justify-content: space-between;
478
- gap: 12px;
479
- margin-top: auto;
480
- padding-top: 12px;
481
- border-top: 1px solid var(--connpass-card-border);
482
- }
483
-
484
- .connpass-btn-primary {
485
- display: inline-flex;
486
- align-items: center;
487
- gap: 6px;
488
- padding: 6px 12px;
489
- border-radius: 999px;
490
- border: none;
491
- background: var(--connpass-button-bg);
492
- color: var(--connpass-button-text);
493
- font-size: 13px;
494
- font-weight: 600;
495
- cursor: pointer;
496
- transition: background 120ms ease;
497
- }
498
-
499
- .connpass-btn-primary:hover {
500
- background: var(--connpass-button-bg-hover);
501
- }
502
-
503
- .connpass-btn-link {
504
- display: inline-flex;
505
- align-items: center;
506
- gap: 4px;
507
- padding: 0;
508
- border: none;
509
- background: transparent;
510
- color: #2563eb;
511
- font-size: 13px;
512
- font-weight: 600;
513
- cursor: pointer;
514
- }
515
-
516
- .connpass-carousel__nav {
517
- position: absolute;
518
- top: 50%;
519
- transform: translateY(-50%);
520
- width: 38px;
521
- height: 38px;
522
- display: inline-flex;
523
- align-items: center;
524
- justify-content: center;
525
- border-radius: 999px;
526
- background: var(--connpass-card-bg);
527
- box-shadow: 0 14px 22px var(--connpass-card-shadow);
528
- border: 1px solid var(--connpass-card-border);
529
- cursor: pointer;
530
- }
531
-
532
- .connpass-carousel__nav:disabled {
533
- opacity: 0.4;
534
- cursor: default;
535
- }
536
-
537
- .connpass-carousel__nav--prev {
538
- left: 8px;
539
- }
540
-
541
- .connpass-carousel__nav--next {
542
- right: 8px;
543
- }
544
-
545
- .connpass-fullscreen {
546
- display: flex;
547
- flex-direction: column;
548
- height: 100vh;
549
- background: var(--connpass-surface);
550
- color: var(--connpass-text-primary);
551
- overflow-y: auto;
552
- overflow-x: hidden;
553
- scrollbar-gutter: stable both-edges;
554
- }
555
-
556
- .connpass-fullscreen__header {
557
- display: flex;
558
- align-items: flex-start;
559
- justify-content: space-between;
560
- flex-wrap: wrap;
561
- gap: 16px;
562
- padding: 20px 26px;
563
- border-bottom: 1px solid var(--connpass-card-border);
564
- }
565
-
566
- .connpass-fullscreen__header-main {
567
- flex: 1 1 auto;
568
- min-width: 220px;
569
- }
570
-
571
- .connpass-fullscreen__header button {
572
- flex: 0 0 auto;
573
- white-space: nowrap;
574
- }
575
-
576
- .connpass-fullscreen__body {
577
- flex: 1 0 auto;
578
- padding: 24px 26px 48px;
579
- }
580
-
581
- .connpass-fullscreen__body section {
582
- margin-bottom: 28px;
583
- }
584
-
585
- .connpass-fullscreen__body h2 {
586
- margin: 0 0 12px;
587
- font-size: 14px;
588
- font-weight: 650;
589
- color: var(--connpass-text-secondary);
590
- text-transform: uppercase;
591
- letter-spacing: 0.04em;
592
- }
593
-
594
- .connpass-fullscreen__body p {
595
- margin: 0;
596
- font-size: 15px;
597
- color: var(--connpass-text-primary);
598
- line-height: 1.7;
599
- }
600
-
601
- .connpass-summary {
602
- display: flex;
603
- flex-direction: column;
604
- gap: 12px;
605
- }
606
-
607
- .connpass-summary__heading {
608
- margin: 16px 0 -4px;
609
- font-size: 14px;
610
- font-weight: 650;
611
- color: var(--connpass-text-secondary);
612
- letter-spacing: 0.02em;
613
- }
614
-
615
- .connpass-summary__heading:first-child {
616
- margin-top: 0;
617
- }
618
-
619
- .connpass-summary__list {
620
- margin: 0;
621
- padding-left: 1.25em;
622
- list-style: disc;
623
- list-style-position: outside;
624
- color: var(--connpass-text-primary);
625
- }
626
-
627
- .connpass-summary__list--compact {
628
- font-size: 13px;
629
- padding-left: 1.2em;
630
- list-style-type: circle;
631
- }
632
-
633
- .connpass-summary__list li + li {
634
- margin-top: 6px;
635
- }
636
-
637
- .connpass-summary__list--compact li + li {
638
- margin-top: 4px;
639
- }
640
-
641
- .connpass-note {
642
- border-left: 3px solid color-mix(in srgb, var(--connpass-accent) 45%, transparent);
643
- padding-left: 10px;
644
- font-size: 13px;
645
- color: var(--connpass-text-secondary);
646
- }
647
-
648
- .connpass-schedule-table {
649
- width: 100%;
650
- margin: 6px 0 0;
651
- border-collapse: collapse;
652
- border: 1px solid color-mix(in srgb, var(--connpass-card-border) 75%, transparent);
653
- border-radius: 14px;
654
- overflow: hidden;
655
- background: color-mix(in srgb, var(--connpass-card-bg) 96%, transparent);
656
- }
657
-
658
- .connpass-schedule-table thead {
659
- background: color-mix(in srgb, var(--connpass-accent) 12%, transparent);
660
- }
661
-
662
- .connpass-schedule-table th,
663
- .connpass-schedule-table td {
664
- padding: 10px 14px;
665
- border-bottom: 1px solid color-mix(in srgb, var(--connpass-card-border) 90%, transparent);
666
- text-align: left;
667
- vertical-align: top;
668
- }
669
-
670
- .connpass-schedule-table thead th {
671
- font-size: 12px;
672
- font-weight: 650;
673
- text-transform: uppercase;
674
- letter-spacing: 0.08em;
675
- color: color-mix(in srgb, var(--connpass-text-secondary) 85%, var(--connpass-text-primary));
676
- }
677
-
678
- .connpass-schedule-table tbody tr:last-child th,
679
- .connpass-schedule-table tbody tr:last-child td {
680
- border-bottom: none;
681
- }
682
-
683
- .connpass-schedule-table__time {
684
- width: 140px;
685
- font-weight: 700;
686
- font-size: 14px;
687
- color: var(--connpass-text-primary);
688
- font-variant-numeric: tabular-nums;
689
- white-space: nowrap;
690
- }
691
-
692
- .connpass-schedule-table__content {
693
- color: var(--connpass-text-secondary);
694
- font-size: 14px;
695
- }
696
-
697
- .connpass-schedule__description {
698
- font-weight: 600;
699
- color: var(--connpass-text-primary);
700
- margin-bottom: 6px;
701
- }
702
-
703
- .connpass-schedule__notes {
704
- margin-top: 6px;
705
- display: flex;
706
- flex-direction: column;
707
- gap: 6px;
708
- }
709
-
710
- .connpass-link {
711
- color: var(--connpass-accent);
712
- text-decoration: underline;
713
- text-decoration-thickness: 1.5px;
714
- text-underline-offset: 4px;
715
- word-break: break-word;
716
- }
717
-
718
- .connpass-link:hover {
719
- color: color-mix(in srgb, var(--connpass-accent) 80%, var(--connpass-text-primary));
720
- }
721
-
722
- .connpass-fullscreen__footer {
723
- display: flex;
724
- flex-wrap: wrap;
725
- gap: 12px;
726
- align-items: center;
727
- justify-content: space-between;
728
- padding: 18px 26px 125px;
729
- border-top: 1px solid var(--connpass-card-border);
730
- background: color-mix(in srgb, var(--connpass-surface) 85%, transparent);
731
- }
732
-
733
- .connpass-tag {
734
- display: inline-flex;
735
- align-items: center;
736
- gap: 5px;
737
- font-size: 12px;
738
- font-weight: 550;
739
- color: var(--connpass-accent);
740
- padding: 4px 8px;
741
- border-radius: 999px;
742
- background: color-mix(in srgb, var(--connpass-accent) 18%, transparent);
743
- overflow: hidden;
744
- text-overflow: ellipsis;
745
- white-space: nowrap;
746
- line-height: 1.3;
747
- width: fit-content;
748
- max-width: 100%;
749
- }
750
-
751
- .connpass-card__catchphrase {
752
- font-size: 13px;
753
- color: var(--connpass-text-secondary);
754
- line-height: 1.5;
755
- display: -webkit-box;
756
- -webkit-line-clamp: 2;
757
- -webkit-box-orient: vertical;
758
- overflow: hidden;
759
- width: 100%;
760
- }
761
-
762
- .connpass-icon {
763
- width: 16px;
764
- height: 16px;
765
- display: inline-block;
766
- flex-shrink: 0;
767
- }
768
-
769
- .connpass-row__label {
770
- display: block;
771
- }
772
-
773
- .connpass-text-sub {
774
- font-size: 12px;
775
- color: var(--connpass-text-muted);
776
- margin-top: 2px;
777
- }
778
-
779
- .connpass-pill-button {
780
- display: inline-flex;
781
- align-items: center;
782
- gap: 4px;
783
- padding: 6px 10px;
784
- border-radius: 999px;
785
- border: 1px solid var(--connpass-card-border);
786
- background: color-mix(in srgb, var(--connpass-accent) 18%, transparent);
787
- color: var(--connpass-accent);
788
- font-size: 12px;
789
- font-weight: 600;
790
- cursor: pointer;
791
- }
792
-
793
- .connpass-presentations {
794
- display: grid;
795
- gap: 12px;
796
- }
797
-
798
- .connpass-presentation {
799
- padding: 14px 16px;
800
- border-radius: 16px;
801
- border: 1px solid var(--connpass-card-border);
802
- background: var(--connpass-card-bg);
803
- box-shadow: 0 16px 30px var(--connpass-card-shadow);
804
- }
805
-
806
- .connpass-presentation__header {
807
- display: flex;
808
- justify-content: space-between;
809
- align-items: baseline;
810
- gap: 12px;
811
- }
812
-
813
- .connpass-presentation__links {
814
- margin-top: 10px;
815
- display: flex;
816
- flex-wrap: wrap;
817
- gap: 8px;
818
- }
819
-
820
- .connpass-link-pill {
821
- display: inline-flex;
822
- align-items: center;
823
- gap: 4px;
824
- padding: 5px 10px;
825
- border-radius: 999px;
826
- border: 1px solid color-mix(in srgb, var(--connpass-accent) 35%, transparent);
827
- background: color-mix(in srgb, var(--connpass-accent) 18%, transparent);
828
- color: var(--connpass-accent);
829
- font-size: 12px;
830
- font-weight: 600;
831
- cursor: pointer;
832
- }
833
-
834
- .connpass-link-pill:hover {
835
- background: color-mix(in srgb, var(--connpass-accent) 26%, transparent);
836
- }
837
-
838
- .connpass-meta {
839
- display: flex;
840
- flex-wrap: wrap;
841
- gap: 18px;
842
- color: var(--connpass-text-secondary);
843
- font-size: 13px;
844
- }
845
-
846
- .connpass-dots {
847
- display: inline-flex;
848
- gap: 6px;
849
- }
850
-
851
- .connpass-dots span {
852
- width: 6px;
853
- height: 6px;
854
- border-radius: 50%;
855
- background: var(--connpass-text-muted);
856
- opacity: 0.3;
857
- animation: connpass-dotPulse 1.2s ease-in-out infinite;
858
- }
859
-
860
- .connpass-dots span:nth-child(2) {
861
- animation-delay: 0.2s;
862
- }
863
-
864
- .connpass-dots span:nth-child(3) {
865
- animation-delay: 0.4s;
866
- }
867
-
868
- @keyframes connpass-dotPulse {
869
- 0%,
870
- 100% {
871
- opacity: 0.2;
872
- }
873
- 50% {
874
- opacity: 0.9;
875
- }
876
- }
877
-
878
- .connpass-detail-grid {
879
- display: grid;
880
- gap: 12px;
881
- }
882
-
883
- .connpass-detail-grid .connpass-row {
884
- margin-top: 0;
885
- }
886
-
887
- @media (max-width: 480px) {
888
- .connpass-card {
889
- flex-basis: 240px;
890
- }
891
-
892
- .connpass-carousel {
893
- padding-left: 12px;
894
- padding-right: 12px;
895
- }
896
-
897
- .connpass-carousel__nav {
898
- display: none;
899
- }
900
-
901
- .connpass-list-card {
902
- flex-direction: column;
903
- }
904
-
905
- .connpass-list-card__media {
906
- width: 100%;
907
- height: auto;
908
- aspect-ratio: 16 / 9;
909
- }
910
- }
911
- </style>
912
- </head>
913
- <body>
914
- <div id="connpass-events-root"></div>
915
- <script>
916
- (function () {
917
- const scrollLockState = {
918
- locked: false,
919
- body: "",
920
- doc: "",
921
- };
922
-
923
- const state = {
924
- displayMode: window.openai?.displayMode ?? "inline",
925
- toolOutput: window.openai?.toolOutput ?? null,
926
- widgetState: normaliseWidgetState(window.openai?.widgetState),
927
- };
928
-
929
- const PARTICIPANTS_ICON_PATH =
930
- "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2m7-14a4 4 0 1 0 0-8 4 4 0 0 0 0 8z";
931
-
932
- function updateBodyScrollLock(shouldLock) {
933
- const body = document.body;
934
- const doc = document.documentElement;
935
- if (!body || !doc) return;
936
-
937
- if (shouldLock && !scrollLockState.locked) {
938
- scrollLockState.locked = true;
939
- scrollLockState.body = body.style.overflow;
940
- scrollLockState.doc = doc.style.overflow;
941
- body.style.overflow = "hidden";
942
- doc.style.overflow = "hidden";
943
- } else if (!shouldLock && scrollLockState.locked) {
944
- scrollLockState.locked = false;
945
- body.style.overflow = scrollLockState.body;
946
- doc.style.overflow = scrollLockState.doc;
947
- }
948
- }
949
-
950
- function normaliseWidgetState(value) {
951
- if (value && typeof value === "object" && value.selectedEventId != null) {
952
- const numeric = Number(value.selectedEventId);
953
- if (Number.isFinite(numeric)) {
954
- return { selectedEventId: numeric };
955
- }
956
- }
957
- return { selectedEventId: null };
958
- }
959
-
960
- function sanitizeEventList(value) {
961
- if (!Array.isArray(value)) {
962
- return [];
963
- }
964
- return value.filter((event) => event && typeof event === "object");
965
- }
966
-
967
- function formatJapaneseDate(ymd) {
968
- if (!ymd) return "";
969
- const match = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.exec(String(ymd));
970
- if (!match) return String(ymd);
971
- const [, year, month, day] = match;
972
- const parsed = new Date(Number(year), Number(month) - 1, Number(day));
973
- if (!Number.isFinite(parsed.valueOf())) {
974
- return String(ymd);
975
- }
976
- try {
977
- return new Intl.DateTimeFormat("ja-JP", {
978
- year: "numeric",
979
- month: "long",
980
- day: "numeric",
981
- weekday: "short",
982
- }).format(parsed);
983
- } catch (error) {
984
- return String(ymd);
985
- }
986
- }
987
-
988
- function buildUpcomingSubtitle(context) {
989
- const parts = [];
990
- if (typeof context.daysAhead === "number" && Number.isFinite(context.daysAhead)) {
991
- parts.push(`あと${context.daysAhead}日`);
992
- }
993
- if (context.todayDate && context.rangeEnd) {
994
- parts.push(`${formatJapaneseDate(context.todayDate)} 〜 ${formatJapaneseDate(context.rangeEnd)}`);
995
- } else if (context.rangeEnd) {
996
- parts.push(`〜 ${formatJapaneseDate(context.rangeEnd)}`);
997
- }
998
- return parts.join(" / ");
999
- }
1000
-
1001
- function normaliseToolOutput() {
1002
- const rawData = state.toolOutput?.data ?? null;
1003
- const metadata = rawData && typeof rawData === "object" ? rawData.metadata ?? null : null;
1004
- const userId = rawData && typeof rawData.userId === "number" ? rawData.userId : null;
1005
-
1006
- if (rawData && Array.isArray(rawData.events)) {
1007
- const events = sanitizeEventList(rawData.events);
1008
- const returnedRaw = Number(rawData.returned ?? events.length ?? 0);
1009
- return {
1010
- variant: "search",
1011
- events,
1012
- returned: Number.isFinite(returnedRaw) ? returnedRaw : events.length,
1013
- sections: [],
1014
- metadata,
1015
- userId,
1016
- };
1017
- }
1018
-
1019
- const todayBlock = rawData && typeof rawData === "object" ? rawData.today ?? null : null;
1020
- const upcomingBlock = rawData && typeof rawData === "object" ? rawData.upcoming ?? null : null;
1021
-
1022
- const todayEvents = todayBlock ? sanitizeEventList(todayBlock.events) : [];
1023
- const upcomingEvents = upcomingBlock ? sanitizeEventList(upcomingBlock.events) : [];
1024
-
1025
- if (todayBlock || upcomingBlock) {
1026
- const sections = [];
1027
- if (todayBlock) {
1028
- sections.push({
1029
- key: "today",
1030
- title: "本日のイベント",
1031
- subtitle: formatJapaneseDate(todayBlock.date) || "本日",
1032
- events: todayEvents,
1033
- emptyText: "今日は参加予定のイベントがありません。",
1034
- });
1035
- }
1036
- if (upcomingBlock) {
1037
- sections.push({
1038
- key: "upcoming",
1039
- title: "今後のイベント",
1040
- subtitle: buildUpcomingSubtitle({
1041
- todayDate: todayBlock?.date ?? null,
1042
- rangeEnd: upcomingBlock.rangeEnd ?? null,
1043
- daysAhead: metadata?.daysAhead,
1044
- }),
1045
- events: upcomingEvents,
1046
- emptyText: "今後予定されているイベントはありません。",
1047
- });
1048
- }
1049
-
1050
- const combinedEvents = todayEvents.concat(upcomingEvents);
1051
- return {
1052
- variant: "agenda",
1053
- events: combinedEvents,
1054
- sections,
1055
- metadata,
1056
- userId,
1057
- returned: combinedEvents.length,
1058
- };
1059
- }
1060
-
1061
- const returnedRaw = rawData ? Number(rawData.returned ?? 0) : 0;
1062
- return {
1063
- variant: "search",
1064
- events: [],
1065
- sections: [],
1066
- metadata,
1067
- userId,
1068
- returned: Number.isFinite(returnedRaw) ? returnedRaw : 0,
1069
- };
1070
- }
1071
-
1072
- function formatDateRange(event) {
1073
- try {
1074
- const start = new Date(event.schedule?.start ?? "");
1075
- const end = new Date(event.schedule?.end ?? "");
1076
- if (!Number.isFinite(start.valueOf()) || !Number.isFinite(end.valueOf())) {
1077
- return "日程未定";
1078
- }
1079
- const sameDay =
1080
- start.getFullYear() === end.getFullYear() &&
1081
- start.getMonth() === end.getMonth() &&
1082
- start.getDate() === end.getDate();
1083
- const dateFormatter = new Intl.DateTimeFormat("ja-JP", {
1084
- month: "short",
1085
- day: "numeric",
1086
- weekday: "short",
1087
- });
1088
- const timeFormatter = new Intl.DateTimeFormat("ja-JP", {
1089
- hour: "2-digit",
1090
- minute: "2-digit",
1091
- });
1092
- const startStr = `${dateFormatter.format(start)} ${timeFormatter.format(start)}`;
1093
- const endStr = sameDay
1094
- ? timeFormatter.format(end)
1095
- : `${dateFormatter.format(end)} ${timeFormatter.format(end)}`;
1096
- return `${startStr} 〜 ${endStr}`;
1097
- } catch (error) {
1098
- return "日程未定";
1099
- }
1100
- }
1101
-
1102
- function participantsLabel(participants) {
1103
- if (!participants) return "0";
1104
- const accepted = Number(participants.accepted ?? 0);
1105
- const limit = Number(participants.limit ?? 0);
1106
- const waiting = Number(participants.waiting ?? 0);
1107
- if (limit > 0) {
1108
- const waitText = waiting > 0 ? ` / 補欠 ${waiting}` : "";
1109
- return `${accepted} / ${limit}${waitText}`;
1110
- }
1111
- return waiting > 0 ? `${accepted} (+補欠 ${waiting})` : `${accepted}`;
1112
- }
1113
-
1114
- const SUMMARY_SECTION_HEADINGS = new Set([
1115
- "概要",
1116
- "この会について",
1117
- "対象",
1118
- "会場",
1119
- "持ち物",
1120
- "参加費",
1121
- "Slack",
1122
- "情報共有",
1123
- "最寄り駅",
1124
- "アクセス",
1125
- "連絡先",
1126
- ]);
1127
-
1128
- const SUMMARY_SKIP_HEADINGS = new Set(["日時", "時間", "内容", "スケジュール"]);
1129
- const SCHEDULE_HEADER_CANDIDATES = new Set(["時間", "内容"]);
1130
-
1131
- function normaliseHeadingText(text) {
1132
- return String(text ?? "")
1133
- .trim()
1134
- .replace(/[::]+$/, "");
1135
- }
1136
-
1137
- function isScheduleLine(value) {
1138
- return /^\d{1,2}:\d{2}/.test(value);
1139
- }
1140
-
1141
- function normaliseScheduleLabel(value) {
1142
- return String(value ?? "")
1143
- .replace(/\s*[-〜–—]\s*/gu, " - ")
1144
- .replace(/\s+/g, " ")
1145
- .trim();
1146
- }
1147
-
1148
- function isBulletLine(value) {
1149
- return /^[\-*・◆■●▶︎]\s*/.test(value);
1150
- }
1151
-
1152
- function stripBullet(value) {
1153
- return String(value ?? "")
1154
- .replace(/^[\-*・◆■●▶︎]\s*/, "")
1155
- .trim();
1156
- }
1157
-
1158
- function isLikelyUrl(value) {
1159
- return /^https?:\/\//i.test(value);
1160
- }
1161
-
1162
- function isNoteLine(value) {
1163
- return /^※/.test(value);
1164
- }
1165
-
1166
- function createExternalLink(url, label) {
1167
- const anchor = document.createElement("a");
1168
- anchor.href = url;
1169
- anchor.textContent = label ?? url;
1170
- anchor.className = "connpass-link";
1171
- anchor.target = "_blank";
1172
- anchor.rel = "noopener noreferrer";
1173
- anchor.addEventListener("click", (event) => {
1174
- event.preventDefault();
1175
- openExternal(url);
1176
- });
1177
- return anchor;
1178
- }
1179
-
1180
- function buildSummaryBlocks(summary) {
1181
- const segments = String(summary ?? "")
1182
- .split(/\n+/)
1183
- .map((segment) => segment.trim())
1184
- .filter(Boolean);
1185
-
1186
- const blocks = [];
1187
- let index = 0;
1188
- let pendingScheduleHeaders = [];
1189
-
1190
- while (index < segments.length) {
1191
- const current = segments[index];
1192
- const headingText = normaliseHeadingText(current);
1193
-
1194
- if (SUMMARY_SKIP_HEADINGS.has(headingText)) {
1195
- if (SCHEDULE_HEADER_CANDIDATES.has(headingText)) {
1196
- if (!pendingScheduleHeaders.includes(headingText)) {
1197
- pendingScheduleHeaders.push(headingText);
1198
- }
1199
- }
1200
- index += 1;
1201
- continue;
1202
- }
1203
-
1204
- if (SUMMARY_SECTION_HEADINGS.has(headingText)) {
1205
- if (!(blocks.length === 0 && headingText === "概要")) {
1206
- blocks.push({ type: "heading", text: headingText });
1207
- }
1208
- pendingScheduleHeaders = [];
1209
- index += 1;
1210
- continue;
1211
- }
1212
-
1213
- if (isScheduleLine(current)) {
1214
- const entries = [];
1215
- while (index < segments.length && isScheduleLine(segments[index])) {
1216
- const timeRaw = segments[index];
1217
- index += 1;
1218
- const entry = {
1219
- time: normaliseScheduleLabel(timeRaw),
1220
- description: undefined,
1221
- details: [],
1222
- };
1223
-
1224
- while (index < segments.length) {
1225
- const peek = segments[index];
1226
- const peekHeading = SUMMARY_SECTION_HEADINGS.has(normaliseHeadingText(peek));
1227
- const peekSkip = SUMMARY_SKIP_HEADINGS.has(normaliseHeadingText(peek));
1228
- if (peekHeading || peekSkip || isScheduleLine(peek)) {
1229
- break;
1230
- }
1231
- if (isBulletLine(peek)) {
1232
- entry.details.push(stripBullet(peek));
1233
- index += 1;
1234
- continue;
1235
- }
1236
- if (isNoteLine(peek)) {
1237
- entry.details.push(peek);
1238
- index += 1;
1239
- continue;
1240
- }
1241
- if (!entry.description) {
1242
- entry.description = peek;
1243
- index += 1;
1244
- continue;
1245
- }
1246
- break;
1247
- }
1248
-
1249
- entries.push(entry);
1250
- }
1251
-
1252
- if (entries.length > 0) {
1253
- const scheduleBlock = { type: "schedule", entries };
1254
- if (pendingScheduleHeaders.length > 0) {
1255
- scheduleBlock.headers = pendingScheduleHeaders.slice(0, 2);
1256
- }
1257
- blocks.push(scheduleBlock);
1258
- }
1259
- pendingScheduleHeaders = [];
1260
- continue;
1261
- }
1262
-
1263
- if (isBulletLine(current)) {
1264
- const items = [];
1265
- while (index < segments.length && isBulletLine(segments[index])) {
1266
- items.push(stripBullet(segments[index]));
1267
- index += 1;
1268
- }
1269
- blocks.push({ type: "list", items });
1270
- pendingScheduleHeaders = [];
1271
- continue;
1272
- }
1273
-
1274
- if (isNoteLine(current)) {
1275
- blocks.push({ type: "note", text: current });
1276
- pendingScheduleHeaders = [];
1277
- index += 1;
1278
- continue;
1279
- }
1280
-
1281
- if (isLikelyUrl(current)) {
1282
- blocks.push({ type: "link", href: current });
1283
- pendingScheduleHeaders = [];
1284
- index += 1;
1285
- continue;
1286
- }
1287
-
1288
- blocks.push({ type: "paragraph", text: current });
1289
- pendingScheduleHeaders = [];
1290
- index += 1;
1291
- }
1292
-
1293
- return blocks;
1294
- }
1295
-
1296
- function renderSummaryContent(target, summary) {
1297
- const blocks = buildSummaryBlocks(summary);
1298
- if (blocks.length === 0) {
1299
- const fallback = document.createElement("p");
1300
- fallback.textContent = summary;
1301
- target.appendChild(fallback);
1302
- return;
1303
- }
1304
-
1305
- const container = document.createElement("div");
1306
- container.className = "connpass-summary";
1307
-
1308
- blocks.forEach((block, order) => {
1309
- if (block.type === "heading") {
1310
- const heading = document.createElement("h3");
1311
- heading.className = "connpass-summary__heading";
1312
- if (order === 0) {
1313
- heading.style.marginTop = "0";
1314
- }
1315
- heading.textContent = block.text;
1316
- container.appendChild(heading);
1317
- return;
1318
- }
1319
-
1320
- if (block.type === "paragraph") {
1321
- const paragraph = document.createElement("p");
1322
- paragraph.textContent = block.text;
1323
- container.appendChild(paragraph);
1324
- return;
1325
- }
1326
-
1327
- if (block.type === "list") {
1328
- const list = document.createElement("ul");
1329
- list.className = "connpass-summary__list";
1330
- block.items.forEach((item) => {
1331
- const li = document.createElement("li");
1332
- li.textContent = item;
1333
- list.appendChild(li);
1334
- });
1335
- container.appendChild(list);
1336
- return;
1337
- }
1338
-
1339
- if (block.type === "link") {
1340
- const linkParagraph = document.createElement("p");
1341
- linkParagraph.appendChild(createExternalLink(block.href, block.label ?? block.href));
1342
- container.appendChild(linkParagraph);
1343
- return;
1344
- }
1345
-
1346
- if (block.type === "note") {
1347
- const note = document.createElement("p");
1348
- note.className = "connpass-note";
1349
- note.textContent = block.text;
1350
- container.appendChild(note);
1351
- return;
1352
- }
1353
-
1354
- if (block.type === "schedule") {
1355
- const table = document.createElement("table");
1356
- table.className = "connpass-schedule-table";
1357
-
1358
- const headers = Array.isArray(block.headers) && block.headers.length > 0
1359
- ? block.headers
1360
- : ["時間", "内容"];
1361
-
1362
- const headerLabels = headers.slice(0, 2);
1363
- while (headerLabels.length < 2) {
1364
- headerLabels.push(headerLabels.length === 0 ? "時間" : "内容");
1365
- }
1366
-
1367
- if (headerLabels.length > 0) {
1368
- const thead = document.createElement("thead");
1369
- const headerRow = document.createElement("tr");
1370
- headerLabels.forEach((label, columnIndex) => {
1371
- const th = document.createElement("th");
1372
- if (columnIndex === 0) {
1373
- th.className = "connpass-schedule-table__time";
1374
- }
1375
- th.textContent = label;
1376
- headerRow.appendChild(th);
1377
- });
1378
- thead.appendChild(headerRow);
1379
- table.appendChild(thead);
1380
- }
1381
-
1382
- const tbody = document.createElement("tbody");
1383
-
1384
- block.entries.forEach((entry) => {
1385
- const row = document.createElement("tr");
1386
-
1387
- const timeCell = document.createElement("th");
1388
- timeCell.scope = "row";
1389
- timeCell.className = "connpass-schedule-table__time";
1390
- timeCell.textContent = entry.time;
1391
- row.appendChild(timeCell);
1392
-
1393
- const detailCell = document.createElement("td");
1394
- detailCell.className = "connpass-schedule-table__content";
1395
-
1396
- if (entry.description) {
1397
- const description = document.createElement("div");
1398
- description.className = "connpass-schedule__description";
1399
- description.textContent = entry.description;
1400
- detailCell.appendChild(description);
1401
- }
1402
-
1403
- const detailItems = entry.details ?? [];
1404
- const listItems = detailItems.filter((item) => !isNoteLine(item));
1405
- const noteItems = detailItems.filter((item) => isNoteLine(item));
1406
-
1407
- if (listItems.length > 0) {
1408
- const detailList = document.createElement("ul");
1409
- detailList.className = "connpass-summary__list connpass-summary__list--compact";
1410
- listItems.forEach((item) => {
1411
- const li = document.createElement("li");
1412
- li.textContent = item;
1413
- detailList.appendChild(li);
1414
- });
1415
- detailCell.appendChild(detailList);
1416
- }
1417
-
1418
- if (noteItems.length > 0) {
1419
- const notes = document.createElement("div");
1420
- notes.className = "connpass-schedule__notes";
1421
- noteItems.forEach((item) => {
1422
- const note = document.createElement("p");
1423
- note.className = "connpass-note";
1424
- note.textContent = item;
1425
- notes.appendChild(note);
1426
- });
1427
- detailCell.appendChild(notes);
1428
- }
1429
-
1430
- row.appendChild(detailCell);
1431
- tbody.appendChild(row);
1432
- });
1433
-
1434
- table.appendChild(tbody);
1435
- container.appendChild(table);
1436
- }
1437
- });
1438
-
1439
- target.appendChild(container);
1440
- }
1441
-
1442
- function eventFallbackLabel(event) {
1443
- if (event.hashTag) {
1444
- return `#${String(event.hashTag).replace(/^#/, "")}`;
1445
- }
1446
- if (event.group?.title) {
1447
- return event.group.title;
1448
- }
1449
- if (event.owner?.displayName) {
1450
- return event.owner.displayName;
1451
- }
1452
- if (event.owner?.nickname) {
1453
- return event.owner.nickname;
1454
- }
1455
- return "Connpass";
1456
- }
1457
-
1458
- function openExternal(url) {
1459
- if (!url) return;
1460
- try {
1461
- if (window.openai?.openExternal) {
1462
- window.openai.openExternal({ href: url });
1463
- } else {
1464
- window.open(url, "_blank", "noopener,noreferrer");
1465
- }
1466
- } catch (error) {
1467
- console.warn("Failed to open external URL", error);
1468
- }
1469
- }
1470
-
1471
- async function setWidgetState(next) {
1472
- state.widgetState = normaliseWidgetState(next);
1473
- if (window.openai?.setWidgetState) {
1474
- try {
1475
- await window.openai.setWidgetState(state.widgetState);
1476
- } catch (error) {
1477
- console.warn("Failed to persist widget state", error);
1478
- }
1479
- }
1480
- }
1481
-
1482
- async function enterFullscreen(eventId) {
1483
- await setWidgetState({ selectedEventId: eventId });
1484
- if (window.openai?.requestDisplayMode) {
1485
- try {
1486
- await window.openai.requestDisplayMode({ mode: "fullscreen" });
1487
- } catch (error) {
1488
- console.warn("Failed to request fullscreen", error);
1489
- }
1490
- }
1491
- render();
1492
- }
1493
-
1494
- async function exitFullscreen() {
1495
- await setWidgetState({ selectedEventId: null });
1496
- if (window.openai?.requestDisplayMode) {
1497
- try {
1498
- await window.openai.requestDisplayMode({ mode: "inline" });
1499
- } catch (error) {
1500
- console.warn("Failed to exit fullscreen", error);
1501
- }
1502
- }
1503
- render();
1504
- }
1505
-
1506
- function createIcon(pathD) {
1507
- const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
1508
- svg.setAttribute("viewBox", "0 0 24 24");
1509
- svg.setAttribute("fill", "none");
1510
- svg.setAttribute("stroke", "currentColor");
1511
- svg.setAttribute("stroke-width", "1.6");
1512
- svg.setAttribute("stroke-linecap", "round");
1513
- svg.setAttribute("stroke-linejoin", "round");
1514
- svg.setAttribute("aria-hidden", "true");
1515
- svg.classList.add("connpass-icon");
1516
- const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
1517
- path.setAttribute("d", pathD);
1518
- svg.appendChild(path);
1519
- return svg;
1520
- }
1521
-
1522
- function renderCarousel(root, events) {
1523
- const wrapper = document.createElement("div");
1524
- wrapper.className = "connpass-carousel";
1525
-
1526
- const scroller = document.createElement("div");
1527
- scroller.className = "connpass-carousel__scroller";
1528
- wrapper.appendChild(scroller);
1529
-
1530
- events.forEach((event) => {
1531
- const card = document.createElement("article");
1532
- card.className = "connpass-card";
1533
-
1534
- if (event.imageUrl) {
1535
- const media = document.createElement("figure");
1536
- media.className = "connpass-card__media";
1537
- const img = document.createElement("img");
1538
- img.src = event.imageUrl;
1539
- img.alt = `${event.title ?? "Connpass イベント"}の画像`;
1540
- img.loading = "lazy";
1541
- media.appendChild(img);
1542
- card.appendChild(media);
1543
- } else {
1544
- const media = document.createElement("div");
1545
- media.className = "connpass-card__media connpass-card__media--fallback";
1546
- const label = document.createElement("span");
1547
- label.className = "connpass-card__media-fallback-label";
1548
- label.textContent = eventFallbackLabel(event);
1549
- media.appendChild(label);
1550
- card.appendChild(media);
1551
- }
1552
-
1553
- const header = document.createElement("div");
1554
- header.className = "connpass-card__content";
1555
-
1556
- const title = document.createElement("h2");
1557
- title.className = "connpass-card__title";
1558
- title.textContent = event.title ?? "タイトル未設定";
1559
- header.appendChild(title);
1560
-
1561
- const badge = document.createElement("span");
1562
- badge.className = "connpass-badge";
1563
- badge.appendChild(createIcon(PARTICIPANTS_ICON_PATH));
1564
- const badgeLabel = document.createElement("span");
1565
- badgeLabel.textContent = `${participantsLabel(event.participants)} 人`;
1566
- badge.appendChild(badgeLabel);
1567
- header.appendChild(badge);
1568
-
1569
- const dateRow = document.createElement("div");
1570
- dateRow.className = "connpass-row";
1571
- dateRow.appendChild(
1572
- createIcon("M8 2v4m8-4v4M3 9h18M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z")
1573
- );
1574
- const dateLabel = document.createElement("div");
1575
- dateLabel.className = "connpass-row__label";
1576
- dateLabel.textContent = formatDateRange(event);
1577
- dateRow.appendChild(dateLabel);
1578
- header.appendChild(dateRow);
1579
-
1580
- if (event.location?.place) {
1581
- const locRow = document.createElement("div");
1582
- locRow.className = "connpass-row";
1583
- locRow.appendChild(
1584
- createIcon("M12 21c-4.2-3.4-7-7.3-7-11a7 7 0 1 1 14 0c0 3.7-2.8 7.6-7 11zm0-9a2 2 0 1 0 0-4 2 2 0 0 0 0 4z")
1585
- );
1586
- const locLabel = document.createElement("div");
1587
- locLabel.className = "connpass-row__label";
1588
- locLabel.textContent = event.location.place;
1589
- if (event.location.address) {
1590
- const sub = document.createElement("span");
1591
- sub.className = "connpass-text-sub";
1592
- sub.textContent = event.location.address;
1593
- locLabel.appendChild(sub);
1594
- }
1595
- locRow.appendChild(locLabel);
1596
- header.appendChild(locRow);
1597
- }
1598
-
1599
- if (event.catchPhrase) {
1600
- const catchPhrase = document.createElement("p");
1601
- catchPhrase.className = "connpass-card__catchphrase";
1602
- catchPhrase.textContent = event.catchPhrase;
1603
- header.appendChild(catchPhrase);
1604
- }
1605
-
1606
- if (event.hashTag) {
1607
- const tag = document.createElement("span");
1608
- tag.className = "connpass-tag";
1609
- tag.textContent = `#${String(event.hashTag).replace(/^#/, "")}`;
1610
- header.appendChild(tag);
1611
- }
1612
-
1613
- card.appendChild(header);
1614
-
1615
- const footer = document.createElement("div");
1616
- footer.className = "connpass-card__footer";
1617
-
1618
- const detailBtn = document.createElement("button");
1619
- detailBtn.type = "button";
1620
- detailBtn.className = "connpass-btn-primary";
1621
- detailBtn.innerHTML =
1622
- '<svg class="connpass-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M21 3 14 10"/><path d="M9 21H3v-6"/><path d="m3 21 7-7"/></svg><span>詳細を見る</span>';
1623
- detailBtn.addEventListener("click", () => enterFullscreen(event.id));
1624
- footer.appendChild(detailBtn);
1625
-
1626
- const linkBtn = document.createElement("button");
1627
- linkBtn.type = "button";
1628
- linkBtn.className = "connpass-btn-link";
1629
- linkBtn.innerHTML =
1630
- '<span>Connpass</span><svg class="connpass-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><path d="m15 3 6 6"/><path d="M21 3h-6v6"/></svg>';
1631
- linkBtn.addEventListener("click", () => openExternal(event.url));
1632
- footer.appendChild(linkBtn);
1633
-
1634
- card.appendChild(footer);
1635
- scroller.appendChild(card);
1636
- });
1637
-
1638
- const prevBtn = document.createElement("button");
1639
- prevBtn.type = "button";
1640
- prevBtn.className = "connpass-carousel__nav connpass-carousel__nav--prev";
1641
- prevBtn.innerHTML =
1642
- '<svg class="connpass-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m15 6-6 6 6 6"/></svg>';
1643
- prevBtn.addEventListener("click", () => {
1644
- scroller.scrollBy({ left: -280, behavior: "smooth" });
1645
- });
1646
-
1647
- const nextBtn = document.createElement("button");
1648
- nextBtn.type = "button";
1649
- nextBtn.className = "connpass-carousel__nav connpass-carousel__nav--next";
1650
- nextBtn.innerHTML =
1651
- '<svg class="connpass-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>';
1652
- nextBtn.addEventListener("click", () => {
1653
- scroller.scrollBy({ left: 280, behavior: "smooth" });
1654
- });
1655
-
1656
- function updateNavButtons() {
1657
- const maxScroll = scroller.scrollWidth - scroller.clientWidth - 1;
1658
- prevBtn.disabled = scroller.scrollLeft <= 0;
1659
- nextBtn.disabled = scroller.scrollLeft >= maxScroll;
1660
- }
1661
-
1662
- scroller.addEventListener("scroll", () => requestAnimationFrame(updateNavButtons));
1663
- window.addEventListener("resize", updateNavButtons, { passive: true });
1664
-
1665
- updateNavButtons();
1666
-
1667
- wrapper.appendChild(prevBtn);
1668
- wrapper.appendChild(nextBtn);
1669
- root.appendChild(wrapper);
1670
- }
1671
-
1672
- function renderPresentations(section, presentations) {
1673
- const list = document.createElement("div");
1674
- list.className = "connpass-presentations";
1675
- presentations.forEach((presentation) => {
1676
- const item = document.createElement("article");
1677
- item.className = "connpass-presentation";
1678
-
1679
- const header = document.createElement("div");
1680
- header.className = "connpass-presentation__header";
1681
- const title = document.createElement("p");
1682
- title.style.margin = "0";
1683
- title.style.fontWeight = "650";
1684
- title.style.fontSize = "15px";
1685
- title.textContent = presentation.title ?? "発表";
1686
- header.appendChild(title);
1687
-
1688
- const order = document.createElement("span");
1689
- order.className = "connpass-pill-button";
1690
- order.textContent = `#${presentation.order ?? ""}`;
1691
- header.appendChild(order);
1692
- item.appendChild(header);
1693
-
1694
- if (presentation.speaker) {
1695
- const speaker = document.createElement("p");
1696
- speaker.className = "connpass-text-sub";
1697
- speaker.style.marginTop = "6px";
1698
- speaker.textContent = presentation.speaker;
1699
- item.appendChild(speaker);
1700
- }
1701
-
1702
- if (presentation.summary) {
1703
- const summary = document.createElement("p");
1704
- summary.style.marginTop = "10px";
1705
- summary.textContent = presentation.summary;
1706
- item.appendChild(summary);
1707
- }
1708
-
1709
- const links = presentation.links ?? {};
1710
- const usableLinks = Object.entries(links).filter(([, href]) => Boolean(href));
1711
- if (usableLinks.length > 0) {
1712
- const linkWrap = document.createElement("div");
1713
- linkWrap.className = "connpass-presentation__links";
1714
- usableLinks.forEach(([key, href]) => {
1715
- const btn = document.createElement("button");
1716
- btn.type = "button";
1717
- btn.className = "connpass-link-pill";
1718
- btn.innerHTML = `${key}<svg class="connpass-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><path d="m15 3 6 6"/><path d="M21 3h-6v6"/></svg>`;
1719
- btn.addEventListener("click", () => openExternal(String(href)));
1720
- linkWrap.appendChild(btn);
1721
- });
1722
- item.appendChild(linkWrap);
1723
- }
1724
-
1725
- list.appendChild(item);
1726
- });
1727
- section.appendChild(list);
1728
- }
1729
-
1730
- function renderMetaRow(path, label, subLabel) {
1731
- const row = document.createElement("div");
1732
- row.className = "connpass-row";
1733
- row.appendChild(createIcon(path));
1734
- const wrap = document.createElement("div");
1735
- wrap.className = "connpass-row__label";
1736
- wrap.textContent = label;
1737
- if (subLabel) {
1738
- const sub = document.createElement("div");
1739
- sub.className = "connpass-text-sub";
1740
- sub.textContent = subLabel;
1741
- wrap.appendChild(sub);
1742
- }
1743
- row.appendChild(wrap);
1744
- return row;
1745
- }
1746
-
1747
- function createAgendaCard(event) {
1748
- const card = document.createElement("article");
1749
- card.className = "connpass-list-card";
1750
-
1751
- const header = document.createElement("div");
1752
- header.className = "connpass-list-card__header";
1753
-
1754
- const media = document.createElement("div");
1755
- media.className = "connpass-list-card__header-media";
1756
- if (event.owner?.imageUrl) {
1757
- const img = document.createElement("img");
1758
- img.src = event.owner.imageUrl;
1759
- img.alt = `${event.owner.displayName || event.owner.nickname || "主催者"}の画像`;
1760
- img.loading = "lazy";
1761
- media.appendChild(img);
1762
- } else if (event.imageUrl) {
1763
- const img = document.createElement("img");
1764
- img.src = event.imageUrl;
1765
- img.alt = `${event.title ?? "Connpass イベント"}の画像`;
1766
- img.loading = "lazy";
1767
- media.appendChild(img);
1768
- } else {
1769
- const fallback = document.createElement("span");
1770
- fallback.className = "connpass-list-card__media-fallback";
1771
- fallback.textContent = eventFallbackLabel(event);
1772
- media.appendChild(fallback);
1773
- }
1774
- header.appendChild(media);
1775
-
1776
- const headerText = document.createElement("div");
1777
- headerText.className = "connpass-list-card__header-text";
1778
-
1779
- const title = document.createElement("h3");
1780
- title.className = "connpass-list-card__title";
1781
- title.textContent = event.title ?? "タイトル未設定";
1782
- headerText.appendChild(title);
1783
-
1784
- const owner = document.createElement("div");
1785
- owner.className = "connpass-list-card__owner";
1786
- owner.textContent = event.owner?.displayName || event.owner?.nickname || "主催者";
1787
- headerText.appendChild(owner);
1788
-
1789
- header.appendChild(headerText);
1790
- card.appendChild(header);
1791
-
1792
- const body = document.createElement("div");
1793
- body.className = "connpass-list-card__body";
1794
-
1795
- if (event.catchPhrase) {
1796
- const catchLine = document.createElement("p");
1797
- catchLine.className = "connpass-list-card__catchphrase";
1798
- catchLine.textContent = event.catchPhrase;
1799
- body.appendChild(catchLine);
1800
- } else if (event.summary) {
1801
- const summary = document.createElement("p");
1802
- summary.className = "connpass-list-card__catchphrase";
1803
- summary.textContent = event.summary;
1804
- body.appendChild(summary);
1805
- }
1806
-
1807
- const metaWrap = document.createElement("div");
1808
- metaWrap.className = "connpass-list-card__meta";
1809
- metaWrap.appendChild(
1810
- renderMetaRow(
1811
- "M8 2v4m8-4v4M3 9h18M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z",
1812
- formatDateRange(event)
1813
- )
1814
- );
1815
-
1816
- if (event.location?.place) {
1817
- metaWrap.appendChild(
1818
- renderMetaRow(
1819
- "M12 21c-4.2-3.4-7-7.3-7-11a7 7 0 1 1 14 0c0 3.7-2.8 7.6-7 11zm0-9a2 2 0 1 0 0-4 2 2 0 0 0 0 4z",
1820
- event.location.place,
1821
- event.location.address
1822
- )
1823
- );
1824
- }
1825
-
1826
- metaWrap.appendChild(
1827
- renderMetaRow(
1828
- PARTICIPANTS_ICON_PATH,
1829
- `${participantsLabel(event.participants)} 人`
1830
- )
1831
- );
1832
-
1833
- body.appendChild(metaWrap);
1834
-
1835
- if (event.hashTag) {
1836
- const tag = document.createElement("span");
1837
- tag.className = "connpass-tag";
1838
- tag.textContent = `#${String(event.hashTag).replace(/^#/, "")}`;
1839
- body.appendChild(tag);
1840
- } else if (event.group?.title) {
1841
- const tag = document.createElement("span");
1842
- tag.className = "connpass-tag";
1843
- tag.textContent = event.group.title;
1844
- body.appendChild(tag);
1845
- }
1846
-
1847
- const actions = document.createElement("div");
1848
- actions.className = "connpass-list-card__actions";
1849
-
1850
- const detailBtn = document.createElement("button");
1851
- detailBtn.type = "button";
1852
- detailBtn.className = "connpass-btn-primary";
1853
- detailBtn.innerHTML =
1854
- '<svg class="connpass-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M21 3 14 10"/><path d="M9 21H3v-6"/><path d="m3 21 7-7"/></svg><span>詳細を見る</span>';
1855
- detailBtn.addEventListener("click", () => enterFullscreen(event.id));
1856
- actions.appendChild(detailBtn);
1857
-
1858
- if (event.url) {
1859
- const linkBtn = document.createElement("button");
1860
- linkBtn.type = "button";
1861
- linkBtn.className = "connpass-btn-link";
1862
- linkBtn.innerHTML =
1863
- '<span>Connpass</span><svg class="connpass-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><path d="m15 3 6 6"/><path d="M21 3h-6v6"/></svg>';
1864
- linkBtn.addEventListener("click", () => openExternal(event.url));
1865
- actions.appendChild(linkBtn);
1866
- }
1867
-
1868
- body.appendChild(actions);
1869
- card.appendChild(body);
1870
-
1871
- return card;
1872
- }
1873
-
1874
- function renderAgenda(root, normalised) {
1875
- const container = document.createElement("div");
1876
- container.className = "connpass-agenda";
1877
-
1878
- const chipLabels = [];
1879
- if (normalised.userId != null) {
1880
- chipLabels.push(`ユーザーID ${normalised.userId}`);
1881
- }
1882
- const meta = normalised.metadata ?? {};
1883
- if (typeof meta.daysAhead === "number" && Number.isFinite(meta.daysAhead)) {
1884
- chipLabels.push(`表示期間 ${meta.daysAhead}日`);
1885
- }
1886
- if (typeof meta.limit === "number" && Number.isFinite(meta.limit)) {
1887
- chipLabels.push(`チェック上限 ${meta.limit}件`);
1888
- }
1889
- if (typeof meta.includePresentations === "boolean") {
1890
- chipLabels.push(meta.includePresentations ? "発表情報あり" : "発表情報なし");
1891
- }
1892
-
1893
- if (chipLabels.length > 0) {
1894
- const chipLine = document.createElement("div");
1895
- chipLine.className = "connpass-agenda__chips";
1896
- chipLabels.forEach((label) => {
1897
- const chip = document.createElement("span");
1898
- chip.className = "connpass-chip";
1899
- chip.textContent = label;
1900
- chipLine.appendChild(chip);
1901
- });
1902
- container.appendChild(chipLine);
1903
- }
1904
-
1905
- normalised.sections.forEach((section) => {
1906
- const sectionEl = document.createElement("section");
1907
- sectionEl.className = "connpass-agenda__section";
1908
-
1909
- const header = document.createElement("header");
1910
- header.className = "connpass-agenda__section-header";
1911
- const heading = document.createElement("h2");
1912
- heading.className = "connpass-agenda__section-title";
1913
- heading.textContent = section.title;
1914
- header.appendChild(heading);
1915
- if (section.subtitle) {
1916
- const subtitle = document.createElement("p");
1917
- subtitle.className = "connpass-agenda__section-subtitle";
1918
- subtitle.textContent = section.subtitle;
1919
- header.appendChild(subtitle);
1920
- }
1921
- sectionEl.appendChild(header);
1922
-
1923
- if (!section.events.length) {
1924
- const empty = document.createElement("div");
1925
- empty.className = "connpass-agenda__empty";
1926
- empty.textContent = section.emptyText ?? "イベントが見つかりません。";
1927
- sectionEl.appendChild(empty);
1928
- container.appendChild(sectionEl);
1929
- return;
1930
- }
1931
-
1932
- const list = document.createElement("div");
1933
- list.className = "connpass-agenda__list";
1934
- section.events.forEach((event) => {
1935
- list.appendChild(createAgendaCard(event));
1936
- });
1937
- sectionEl.appendChild(list);
1938
- container.appendChild(sectionEl);
1939
- });
1940
-
1941
- const metaLineItems = [];
1942
- if (typeof meta.inspected === "number" && Number.isFinite(meta.inspected)) {
1943
- if (typeof meta.limit === "number" && Number.isFinite(meta.limit)) {
1944
- metaLineItems.push(`取得対象 ${meta.inspected}/${meta.limit}件`);
1945
- } else {
1946
- metaLineItems.push(`取得対象 ${meta.inspected}件`);
1947
- }
1948
- }
1949
-
1950
- if (metaLineItems.length > 0) {
1951
- const metaLine = document.createElement("div");
1952
- metaLine.className = "connpass-agenda__meta";
1953
- metaLine.textContent = metaLineItems.join(" ・ ");
1954
- container.appendChild(metaLine);
1955
- }
1956
-
1957
- root.appendChild(container);
1958
- }
1959
-
1960
- function renderInline(root, normalised) {
1961
- if (normalised.variant === "agenda") {
1962
- renderAgenda(root, normalised);
1963
- return;
1964
- }
1965
- renderCarousel(root, normalised.events);
1966
- }
1967
-
1968
- function renderFullscreen(root, event) {
1969
- const container = document.createElement("div");
1970
- container.className = "connpass-fullscreen";
1971
-
1972
- if (event.imageUrl) {
1973
- const hero = document.createElement("figure");
1974
- hero.className = "connpass-hero";
1975
- const img = document.createElement("img");
1976
- img.src = event.imageUrl;
1977
- img.alt = `${event.title ?? "Connpass イベント"}の画像`;
1978
- hero.appendChild(img);
1979
-
1980
- const heroBadge = document.createElement("span");
1981
- heroBadge.className = "connpass-hero__badge";
1982
- heroBadge.innerHTML =
1983
- '<svg class="connpass-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><path d="M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z"/></svg>';
1984
- const heroBadgeText = document.createElement("span");
1985
- heroBadgeText.textContent = `${participantsLabel(event.participants)} 人`;
1986
- heroBadge.appendChild(heroBadgeText);
1987
- hero.appendChild(heroBadge);
1988
-
1989
- container.appendChild(hero);
1990
- } else {
1991
- const hero = document.createElement("div");
1992
- hero.className = "connpass-hero connpass-hero--fallback";
1993
- const heroLabel = document.createElement("span");
1994
- heroLabel.className = "connpass-hero__label";
1995
- heroLabel.textContent = eventFallbackLabel(event);
1996
- hero.appendChild(heroLabel);
1997
- const heroBadge = document.createElement("span");
1998
- heroBadge.className = "connpass-hero__badge";
1999
- heroBadge.innerHTML =
2000
- '<svg class="connpass-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><path d="M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z"/></svg>';
2001
- const heroBadgeText = document.createElement("span");
2002
- heroBadgeText.textContent = `${participantsLabel(event.participants)} 人`;
2003
- heroBadge.appendChild(heroBadgeText);
2004
- hero.appendChild(heroBadge);
2005
- container.appendChild(hero);
2006
- }
2007
-
2008
- const header = document.createElement("header");
2009
- header.className = "connpass-fullscreen__header";
2010
- const titleWrap = document.createElement("div");
2011
- titleWrap.className = "connpass-fullscreen__header-main";
2012
- const label = document.createElement("p");
2013
- label.className = "connpass-text-sub";
2014
- label.textContent = "Connpass イベント";
2015
- titleWrap.appendChild(label);
2016
- const title = document.createElement("h1");
2017
- title.textContent = event.title ?? "Connpass イベント";
2018
- title.style.margin = "6px 0 0";
2019
- title.style.fontSize = "26px";
2020
- title.style.lineHeight = "1.2";
2021
- title.style.fontWeight = "700";
2022
- titleWrap.appendChild(title);
2023
- header.appendChild(titleWrap);
2024
-
2025
- const closeBtn = document.createElement("button");
2026
- closeBtn.type = "button";
2027
- closeBtn.className = "connpass-pill-button connpass-pill-button--ghost";
2028
- closeBtn.innerHTML =
2029
- '<svg class="connpass-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m18 6-12 12"/><path d="m6 6 12 12"/></svg><span>閉じる</span>';
2030
- closeBtn.addEventListener("click", exitFullscreen);
2031
- header.appendChild(closeBtn);
2032
- container.appendChild(header);
2033
-
2034
- const body = document.createElement("div");
2035
- body.className = "connpass-fullscreen__body";
2036
-
2037
- const schedule = document.createElement("section");
2038
- const meta = document.createElement("div");
2039
- meta.className = "connpass-meta";
2040
- meta.appendChild(renderMetaRow("M8 2v4m8-4v4M3 9h18M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z", formatDateRange(event)));
2041
- meta.appendChild(
2042
- renderMetaRow(
2043
- "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2m7-14a4 4 0 1 0 0-8 4 4 0 0 0 0 8z",
2044
- `参加者 ${participantsLabel(event.participants)} 人`
2045
- )
2046
- );
2047
- if (event.location?.place) {
2048
- meta.appendChild(
2049
- renderMetaRow(
2050
- "M12 21c-4.2-3.4-7-7.3-7-11a7 7 0 1 1 14 0c0 3.7-2.8 7.6-7 11zm0-9a2 2 0 1 0 0-4 2 2 0 0 0 0 4z",
2051
- event.location.place,
2052
- event.location.address
2053
- )
2054
- );
2055
- }
2056
- schedule.appendChild(meta);
2057
- body.appendChild(schedule);
2058
-
2059
- const infoSection = document.createElement("section");
2060
- infoSection.className = "connpass-detail";
2061
- const infoHeading = document.createElement("h2");
2062
- infoHeading.textContent = "主催・関連";
2063
- infoSection.appendChild(infoHeading);
2064
-
2065
- const infoGrid = document.createElement("div");
2066
- infoGrid.className = "connpass-detail-grid";
2067
-
2068
- if (event.owner) {
2069
- const ownerLabel = event.owner.displayName || event.owner.nickname;
2070
- const ownerRow = renderMetaRow(
2071
- "M16 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2m8-10a4 4 0 1 0 0-8 4 4 0 0 0 0 8z",
2072
- `主催 ${ownerLabel}`,
2073
- event.owner.displayName && event.owner.nickname && event.owner.displayName !== event.owner.nickname
2074
- ? `@${event.owner.nickname}`
2075
- : undefined
2076
- );
2077
- infoGrid.appendChild(ownerRow);
2078
- }
2079
-
2080
- if (event.group?.title) {
2081
- const groupRow = renderMetaRow(
2082
- "M4 4h6l2 2h8v14H4z",
2083
- `グループ ${event.group.title}`,
2084
- event.group.url ? "Connpass グループページ" : undefined
2085
- );
2086
- if (event.group?.url) {
2087
- const buttonWrap = document.createElement("div");
2088
- buttonWrap.style.marginTop = "6px";
2089
- buttonWrap.style.display = "flex";
2090
- buttonWrap.style.gap = "8px";
2091
- const groupBtn = document.createElement("button");
2092
- groupBtn.type = "button";
2093
- groupBtn.className = "connpass-btn-link";
2094
- groupBtn.innerHTML =
2095
- '<span>グループを見る</span><svg class="connpass-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><path d="m15 3 6 6"/><path d="M21 3h-6v6"/></svg>';
2096
- groupBtn.addEventListener("click", () => openExternal(event.group.url));
2097
- buttonWrap.appendChild(groupBtn);
2098
- const labelWrap = groupRow.querySelector(".connpass-row__label");
2099
- if (labelWrap) {
2100
- labelWrap.appendChild(buttonWrap);
2101
- } else {
2102
- groupRow.appendChild(buttonWrap);
2103
- }
2104
- }
2105
- infoGrid.appendChild(groupRow);
2106
- }
2107
-
2108
- if (event.hashTag) {
2109
- const tagRow = renderMetaRow(
2110
- "M4 9h16M4 15h16M10 3 8 21M16 3l-2 18",
2111
- `ハッシュタグ #${String(event.hashTag).replace(/^#/, "")}`
2112
- );
2113
- infoGrid.appendChild(tagRow);
2114
- }
2115
-
2116
- if (infoGrid.childElementCount > 0) {
2117
- infoSection.appendChild(infoGrid);
2118
- body.appendChild(infoSection);
2119
- }
2120
-
2121
- if (event.catchPhrase) {
2122
- const section = document.createElement("section");
2123
- const heading = document.createElement("h2");
2124
- heading.textContent = "キャッチコピー";
2125
- section.appendChild(heading);
2126
- const paragraph = document.createElement("p");
2127
- paragraph.textContent = event.catchPhrase;
2128
- section.appendChild(paragraph);
2129
- body.appendChild(section);
2130
- }
2131
-
2132
- if (event.summary) {
2133
- const section = document.createElement("section");
2134
- const heading = document.createElement("h2");
2135
- heading.textContent = "概要";
2136
- section.appendChild(heading);
2137
- renderSummaryContent(section, event.summary);
2138
- body.appendChild(section);
2139
- }
2140
-
2141
- if (Array.isArray(event.presentations) && event.presentations.length > 0) {
2142
- const section = document.createElement("section");
2143
- const heading = document.createElement("h2");
2144
- heading.textContent = "発表";
2145
- section.appendChild(heading);
2146
- renderPresentations(section, event.presentations);
2147
- body.appendChild(section);
2148
- }
2149
-
2150
- container.appendChild(body);
2151
-
2152
- const footer = document.createElement("footer");
2153
- footer.className = "connpass-fullscreen__footer";
2154
- const owner = document.createElement("div");
2155
- owner.className = "connpass-text-sub";
2156
- const ownerName = event.owner?.displayName ?? event.owner?.nickname ?? "主催者";
2157
- const ownerLabel = document.createElement("span");
2158
- ownerLabel.textContent = "主催: ";
2159
- owner.appendChild(ownerLabel);
2160
- const ownerStrong = document.createElement("strong");
2161
- ownerStrong.textContent = ownerName;
2162
- ownerStrong.style.color = "var(--connpass-text-primary)";
2163
- ownerStrong.style.fontWeight = "650";
2164
- owner.appendChild(ownerStrong);
2165
- if (event.group?.title) {
2166
- const groupSpan = document.createElement("span");
2167
- groupSpan.textContent = `(${event.group.title})`;
2168
- owner.appendChild(groupSpan);
2169
- }
2170
- footer.appendChild(owner);
2171
-
2172
- const actions = document.createElement("div");
2173
- actions.style.display = "flex";
2174
- actions.style.gap = "12px";
2175
-
2176
- const connpassBtn = document.createElement("button");
2177
- connpassBtn.type = "button";
2178
- connpassBtn.className = "connpass-btn-primary";
2179
- connpassBtn.innerHTML =
2180
- '<span>Connpass でイベントを見る</span><svg class="connpass-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><path d="m15 3 6 6"/><path d="M21 3h-6v6"/></svg>';
2181
- connpassBtn.addEventListener("click", () => openExternal(event.url));
2182
- actions.appendChild(connpassBtn);
2183
-
2184
- footer.appendChild(actions);
2185
- container.appendChild(footer);
2186
-
2187
- root.appendChild(container);
2188
- container.scrollTop = 0;
2189
- }
2190
-
2191
- function render() {
2192
- const root = document.getElementById("connpass-events-root");
2193
- if (!root) return;
2194
- root.innerHTML = "";
2195
-
2196
- const normalised = normaliseToolOutput();
2197
- const events = normalised.events;
2198
- const hasSearched = Boolean(state.toolOutput);
2199
-
2200
- if (normalised.variant !== "agenda" && events.length === 0) {
2201
- const empty = document.createElement("div");
2202
- empty.className = "connpass-empty";
2203
- if (!hasSearched) {
2204
- empty.setAttribute("aria-busy", "true");
2205
- const message = document.createElement("span");
2206
- message.textContent = "Connpassイベントを探しています";
2207
- empty.appendChild(message);
2208
- const dots = document.createElement("span");
2209
- dots.className = "connpass-dots";
2210
- dots.innerHTML = "<span></span><span></span><span></span>";
2211
- empty.appendChild(dots);
2212
- } else {
2213
- const returned = Number(normalised.returned ?? 0);
2214
- empty.textContent =
2215
- returned === 0
2216
- ? "イベントが見つかりませんでした。条件を調整して再検索してください。"
2217
- : "条件に一致するイベントがありません。別のキーワードをお試しください。";
2218
- }
2219
- root.appendChild(empty);
2220
- updateBodyScrollLock(false);
2221
- return;
2222
- }
2223
-
2224
- if (normalised.variant === "agenda" && normalised.sections.length === 0 && hasSearched) {
2225
- const empty = document.createElement("div");
2226
- empty.className = "connpass-empty";
2227
- empty.textContent = "表示できるイベントがありません。Connpassの予定を確認してください。";
2228
- root.appendChild(empty);
2229
- updateBodyScrollLock(false);
2230
- return;
2231
- }
2232
-
2233
- const selectedId = state.widgetState?.selectedEventId ?? null;
2234
- const selectedEvent = events.find((event) => event.id === selectedId) ?? null;
2235
-
2236
- updateBodyScrollLock(state.displayMode === "fullscreen" && Boolean(selectedEvent));
2237
-
2238
- if (state.displayMode === "fullscreen" && selectedEvent) {
2239
- renderFullscreen(root, selectedEvent);
2240
- return;
2241
- }
2242
-
2243
- renderInline(root, normalised);
2244
- updateBodyScrollLock(false);
2245
- }
2246
-
2247
- window.addEventListener(
2248
- "openai:set_globals",
2249
- (event) => {
2250
- const globals = event.detail?.globals ?? {};
2251
- if (Object.prototype.hasOwnProperty.call(globals, "toolOutput")) {
2252
- state.toolOutput = globals.toolOutput;
2253
- }
2254
- if (Object.prototype.hasOwnProperty.call(globals, "displayMode")) {
2255
- state.displayMode = globals.displayMode ?? "inline";
2256
- }
2257
- if (Object.prototype.hasOwnProperty.call(globals, "widgetState")) {
2258
- state.widgetState = normaliseWidgetState(globals.widgetState);
2259
- }
2260
- render();
2261
- },
2262
- { passive: true }
2263
- );
2264
-
2265
- render();
2266
- })();
2267
- </script>
2268
- </body>
2269
- </html>