@real1ty-obsidian-plugins/utils 2.31.0 → 2.33.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.
|
@@ -47,6 +47,10 @@ export interface WhatsNewModalConfig {
|
|
|
47
47
|
* Example: "https://docs.example.com" or "https://docs.example.com/"
|
|
48
48
|
*/
|
|
49
49
|
documentation: string;
|
|
50
|
+
/**
|
|
51
|
+
* URL to GitHub repository.
|
|
52
|
+
*/
|
|
53
|
+
github: string;
|
|
50
54
|
/**
|
|
51
55
|
* URL to tools page showcasing all plugins and productivity tools.
|
|
52
56
|
* Defaults to DEFAULT_WHATS_NEW_LINKS.TOOLS if not provided.
|
|
@@ -62,6 +66,260 @@ export interface WhatsNewModalConfig {
|
|
|
62
66
|
/**
|
|
63
67
|
* Generic "What's New" modal that displays changelog entries between versions.
|
|
64
68
|
* Supports custom CSS prefixes, plugin names, and configurable links.
|
|
69
|
+
*
|
|
70
|
+
* ## CSS Classes
|
|
71
|
+
*
|
|
72
|
+
* This modal uses the following CSS classes (with your custom prefix).
|
|
73
|
+
* Replace `{prefix}` with your `cssPrefix` value (e.g., "my-plugin").
|
|
74
|
+
*
|
|
75
|
+
* ### Main Container
|
|
76
|
+
* - `.{prefix}-whats-new-modal` - Applied to the main content element
|
|
77
|
+
* - `.{prefix}-whats-new-modal .modal` - Modal dialog styling (max-width, width)
|
|
78
|
+
*
|
|
79
|
+
* ### Title and Subtitle
|
|
80
|
+
* - Modal title is set via `setTitle()` - Obsidian handles the styling and X close button
|
|
81
|
+
* - `.{prefix}-whats-new-subtitle` - Subtitle text ("Changes since vX.X.X")
|
|
82
|
+
*
|
|
83
|
+
* ### Support Section
|
|
84
|
+
* - `.{prefix}-whats-new-support` - Support section container
|
|
85
|
+
* - Contains donation, tools, and YouTube links
|
|
86
|
+
* - Should have background, padding, border-radius
|
|
87
|
+
* - `.{prefix}-whats-new-support h3` - Support section heading
|
|
88
|
+
* - `.{prefix}-whats-new-support p` - Support section paragraph text (one per row)
|
|
89
|
+
* - `.{prefix}-whats-new-support a` - Links in support section (consistent styling)
|
|
90
|
+
* - `.{prefix}-whats-new-support a:hover` - Link hover state
|
|
91
|
+
*
|
|
92
|
+
* ### Changelog Content
|
|
93
|
+
* - `.{prefix}-whats-new-content` - Changelog content container
|
|
94
|
+
* - Should have max-height, overflow-y: auto for scrolling
|
|
95
|
+
* - `.{prefix}-whats-new-content h2` - Version headings in changelog
|
|
96
|
+
* - `.{prefix}-whats-new-content h3` - Section headings in changelog
|
|
97
|
+
* - `.{prefix}-whats-new-content ul` - Changelog lists
|
|
98
|
+
* - `.{prefix}-whats-new-content li` - Changelog list items
|
|
99
|
+
* - `.{prefix}-whats-new-content code` - Inline code in changelog
|
|
100
|
+
* - `.{prefix}-whats-new-content pre` - Code blocks in changelog
|
|
101
|
+
* - `.{prefix}-whats-new-content a.external-link` - External links (auto-added)
|
|
102
|
+
* - `.{prefix}-whats-new-empty` - Empty state message
|
|
103
|
+
*
|
|
104
|
+
* ### Sticky Footer
|
|
105
|
+
* - `.{prefix}-whats-new-sticky-footer` - Footer container (should be sticky)
|
|
106
|
+
* - Has border-top to separate from content
|
|
107
|
+
* - `.{prefix}-whats-new-buttons` - Button container
|
|
108
|
+
* - `.{prefix}-whats-new-buttons button` - Individual buttons
|
|
109
|
+
*
|
|
110
|
+
* ## Example CSS Implementation
|
|
111
|
+
*
|
|
112
|
+
* ```css
|
|
113
|
+
* // Main Container
|
|
114
|
+
* .my-plugin-whats-new-modal .modal {
|
|
115
|
+
* max-width: 800px;
|
|
116
|
+
* width: 90%;
|
|
117
|
+
* }
|
|
118
|
+
*
|
|
119
|
+
* // Plugin Name Link (in title)
|
|
120
|
+
* .my-plugin-whats-new-plugin-name {
|
|
121
|
+
* color: var(--link-color);
|
|
122
|
+
* text-decoration: none;
|
|
123
|
+
* transition: all 0.2s ease;
|
|
124
|
+
* position: relative;
|
|
125
|
+
* font-weight: 600;
|
|
126
|
+
* }
|
|
127
|
+
*
|
|
128
|
+
* .my-plugin-whats-new-plugin-name:hover {
|
|
129
|
+
* color: var(--link-color-hover);
|
|
130
|
+
* text-decoration: none;
|
|
131
|
+
* }
|
|
132
|
+
*
|
|
133
|
+
* .my-plugin-whats-new-plugin-name::after {
|
|
134
|
+
* content: '';
|
|
135
|
+
* position: absolute;
|
|
136
|
+
* bottom: -2px;
|
|
137
|
+
* left: 0;
|
|
138
|
+
* width: 0;
|
|
139
|
+
* height: 2px;
|
|
140
|
+
* background-color: var(--interactive-accent);
|
|
141
|
+
* transition: width 0.3s ease;
|
|
142
|
+
* }
|
|
143
|
+
*
|
|
144
|
+
* .my-plugin-whats-new-plugin-name:hover::after {
|
|
145
|
+
* width: 100%;
|
|
146
|
+
* }
|
|
147
|
+
*
|
|
148
|
+
* // Subtitle
|
|
149
|
+
* .my-plugin-whats-new-subtitle {
|
|
150
|
+
* color: var(--text-muted);
|
|
151
|
+
* font-size: 0.9rem;
|
|
152
|
+
* margin: 0 0 1rem 0;
|
|
153
|
+
* }
|
|
154
|
+
*
|
|
155
|
+
* // Support Section (with donation, tools, and YouTube links)
|
|
156
|
+
* .my-plugin-whats-new-support {
|
|
157
|
+
* margin: 0 0 1rem 0;
|
|
158
|
+
* padding: 1rem;
|
|
159
|
+
* background-color: var(--background-secondary);
|
|
160
|
+
* border-radius: 8px;
|
|
161
|
+
* }
|
|
162
|
+
*
|
|
163
|
+
* .my-plugin-whats-new-support h3 {
|
|
164
|
+
* margin-top: 0;
|
|
165
|
+
* margin-bottom: 0.5rem;
|
|
166
|
+
* font-size: 1rem;
|
|
167
|
+
* }
|
|
168
|
+
*
|
|
169
|
+
* .my-plugin-whats-new-support p {
|
|
170
|
+
* margin: 0.5rem 0;
|
|
171
|
+
* color: var(--text-normal);
|
|
172
|
+
* }
|
|
173
|
+
*
|
|
174
|
+
* .my-plugin-whats-new-support a {
|
|
175
|
+
* color: var(--link-color);
|
|
176
|
+
* text-decoration: none;
|
|
177
|
+
* transition: all 0.2s ease;
|
|
178
|
+
* position: relative;
|
|
179
|
+
* }
|
|
180
|
+
*
|
|
181
|
+
* .my-plugin-whats-new-support a:hover {
|
|
182
|
+
* color: var(--link-color-hover);
|
|
183
|
+
* text-decoration: none;
|
|
184
|
+
* }
|
|
185
|
+
*
|
|
186
|
+
* .my-plugin-whats-new-support a::after {
|
|
187
|
+
* content: '';
|
|
188
|
+
* position: absolute;
|
|
189
|
+
* bottom: -2px;
|
|
190
|
+
* left: 0;
|
|
191
|
+
* width: 0;
|
|
192
|
+
* height: 2px;
|
|
193
|
+
* background-color: var(--interactive-accent);
|
|
194
|
+
* transition: width 0.3s ease;
|
|
195
|
+
* }
|
|
196
|
+
*
|
|
197
|
+
* .my-plugin-whats-new-support a:hover::after {
|
|
198
|
+
* width: 100%;
|
|
199
|
+
* }
|
|
200
|
+
*
|
|
201
|
+
* // Changelog Content (Scrollable Area)
|
|
202
|
+
* .my-plugin-whats-new-content {
|
|
203
|
+
* max-height: 400px;
|
|
204
|
+
* overflow-y: auto;
|
|
205
|
+
* margin-bottom: 1rem;
|
|
206
|
+
* padding-right: 0.5rem;
|
|
207
|
+
* border-radius: 8px;
|
|
208
|
+
* }
|
|
209
|
+
*
|
|
210
|
+
* .my-plugin-whats-new-content h2 {
|
|
211
|
+
* font-size: 1.3rem;
|
|
212
|
+
* margin-top: 1.5rem;
|
|
213
|
+
* margin-bottom: 0.5rem;
|
|
214
|
+
* color: var(--text-accent);
|
|
215
|
+
* }
|
|
216
|
+
*
|
|
217
|
+
* .my-plugin-whats-new-content h3 {
|
|
218
|
+
* font-size: 1.1rem;
|
|
219
|
+
* margin-top: 1rem;
|
|
220
|
+
* margin-bottom: 0.5rem;
|
|
221
|
+
* }
|
|
222
|
+
*
|
|
223
|
+
* .my-plugin-whats-new-content ul {
|
|
224
|
+
* padding-left: 1.5rem;
|
|
225
|
+
* }
|
|
226
|
+
*
|
|
227
|
+
* .my-plugin-whats-new-content li {
|
|
228
|
+
* margin-bottom: 0.5rem;
|
|
229
|
+
* line-height: 1.6;
|
|
230
|
+
* }
|
|
231
|
+
*
|
|
232
|
+
* .my-plugin-whats-new-content code {
|
|
233
|
+
* background: var(--code-background);
|
|
234
|
+
* padding: 0.2em 0.4em;
|
|
235
|
+
* border-radius: 3px;
|
|
236
|
+
* font-size: 0.9em;
|
|
237
|
+
* }
|
|
238
|
+
*
|
|
239
|
+
* .my-plugin-whats-new-content pre {
|
|
240
|
+
* background: var(--code-background);
|
|
241
|
+
* padding: 1rem;
|
|
242
|
+
* border-radius: 6px;
|
|
243
|
+
* overflow-x: auto;
|
|
244
|
+
* }
|
|
245
|
+
*
|
|
246
|
+
* .my-plugin-whats-new-content a.external-link {
|
|
247
|
+
* color: var(--link-external-color);
|
|
248
|
+
* }
|
|
249
|
+
*
|
|
250
|
+
* .my-plugin-whats-new-content a.external-link::after {
|
|
251
|
+
* content: "↗";
|
|
252
|
+
* margin-left: 0.2em;
|
|
253
|
+
* font-size: 0.8em;
|
|
254
|
+
* }
|
|
255
|
+
*
|
|
256
|
+
* .my-plugin-whats-new-empty {
|
|
257
|
+
* text-align: center;
|
|
258
|
+
* color: var(--text-muted);
|
|
259
|
+
* padding: 2rem;
|
|
260
|
+
* font-style: italic;
|
|
261
|
+
* }
|
|
262
|
+
*
|
|
263
|
+
* // Sticky Footer
|
|
264
|
+
* .my-plugin-whats-new-sticky-footer {
|
|
265
|
+
* position: sticky;
|
|
266
|
+
* bottom: 0;
|
|
267
|
+
* background: var(--background-primary);
|
|
268
|
+
* padding-top: 0.75rem;
|
|
269
|
+
* margin-top: 0;
|
|
270
|
+
* z-index: 10;
|
|
271
|
+
* border-top: 1px solid var(--background-modifier-border);
|
|
272
|
+
* }
|
|
273
|
+
*
|
|
274
|
+
* .my-plugin-whats-new-buttons {
|
|
275
|
+
* display: flex;
|
|
276
|
+
* gap: 0.5rem;
|
|
277
|
+
* justify-content: space-between;
|
|
278
|
+
* flex-wrap: wrap;
|
|
279
|
+
* padding-bottom: 0.5rem;
|
|
280
|
+
* width: 100%;
|
|
281
|
+
* }
|
|
282
|
+
*
|
|
283
|
+
* .my-plugin-whats-new-buttons button {
|
|
284
|
+
* flex: 1;
|
|
285
|
+
* min-width: 0;
|
|
286
|
+
* padding: 0.5rem 1rem;
|
|
287
|
+
* border-radius: 4px;
|
|
288
|
+
* cursor: pointer;
|
|
289
|
+
* border: 1px solid var(--background-modifier-border);
|
|
290
|
+
* background: var(--interactive-normal);
|
|
291
|
+
* color: var(--text-normal);
|
|
292
|
+
* transition: all 0.2s ease;
|
|
293
|
+
* text-align: center;
|
|
294
|
+
* }
|
|
295
|
+
*
|
|
296
|
+
* .my-plugin-whats-new-buttons button:hover {
|
|
297
|
+
* background: var(--interactive-hover);
|
|
298
|
+
* border-color: var(--interactive-accent);
|
|
299
|
+
* transform: translateY(-1px);
|
|
300
|
+
* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
301
|
+
* }
|
|
302
|
+
*
|
|
303
|
+
* .my-plugin-whats-new-buttons button:active {
|
|
304
|
+
* transform: translateY(0);
|
|
305
|
+
* box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
|
306
|
+
* }
|
|
307
|
+
* ```
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* ```typescript
|
|
311
|
+
* const modal = new WhatsNewModal(app, plugin, {
|
|
312
|
+
* cssPrefix: "my-plugin",
|
|
313
|
+
* pluginName: "My Plugin",
|
|
314
|
+
* changelogContent: rawChangelog,
|
|
315
|
+
* links: {
|
|
316
|
+
* support: "https://...",
|
|
317
|
+
* changelog: "https://...",
|
|
318
|
+
* documentation: "https://..."
|
|
319
|
+
* }
|
|
320
|
+
* }, "1.0.0", "2.0.0");
|
|
321
|
+
* modal.open();
|
|
322
|
+
* ```
|
|
65
323
|
*/
|
|
66
324
|
export declare class WhatsNewModal extends Modal {
|
|
67
325
|
private plugin;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whats-new-modal.d.ts","sourceRoot":"","sources":["../../src/components/whats-new-modal.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"whats-new-modal.d.ts","sourceRoot":"","sources":["../../src/components/whats-new-modal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAoB,KAAK,EAAE,MAAM,UAAU,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,uBAAuB;IACnC;;OAEG;;IAGH;;;OAGG;;CAEM,CAAC;AAEX,MAAM,WAAW,mBAAmB;IACnC;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,gBAAgB,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,KAAK,EAAE;QACN;;WAEG;QACH,OAAO,EAAE,MAAM,CAAC;QAEhB;;WAEG;QACH,SAAS,EAAE,MAAM,CAAC;QAElB;;;WAGG;QACH,aAAa,EAAE,MAAM,CAAC;QAEtB;;WAEG;QACH,MAAM,EAAE,MAAM,CAAC;QAEf;;;WAGG;QACH,KAAK,CAAC,EAAE,MAAM,CAAC;QAEf;;;WAGG;QACH,OAAO,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiQG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAGtC,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,SAAS;gBAJjB,GAAG,EAAE,GAAG,EACA,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,mBAAmB,EAC3B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM;IAK1B;;OAEG;IACH,OAAO,CAAC,GAAG;IAIX;;OAEG;IACH,OAAO,CAAC,MAAM;IAId;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;IAmC5B,MAAM;IAoJZ,OAAO;CAGP"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __awaiter } from "tslib";
|
|
2
|
+
import { formatChangelogSections, getChangelogSince } from "@real1ty-obsidian-plugins/utils";
|
|
2
3
|
import { MarkdownRenderer, Modal } from "obsidian";
|
|
3
|
-
import { formatChangelogSections, getChangelogSince } from "../string/changelog-parser";
|
|
4
4
|
/**
|
|
5
5
|
* Default URLs for the What's New modal.
|
|
6
6
|
* These can be overridden in the config.
|
|
@@ -19,6 +19,260 @@ export const DEFAULT_WHATS_NEW_LINKS = {
|
|
|
19
19
|
/**
|
|
20
20
|
* Generic "What's New" modal that displays changelog entries between versions.
|
|
21
21
|
* Supports custom CSS prefixes, plugin names, and configurable links.
|
|
22
|
+
*
|
|
23
|
+
* ## CSS Classes
|
|
24
|
+
*
|
|
25
|
+
* This modal uses the following CSS classes (with your custom prefix).
|
|
26
|
+
* Replace `{prefix}` with your `cssPrefix` value (e.g., "my-plugin").
|
|
27
|
+
*
|
|
28
|
+
* ### Main Container
|
|
29
|
+
* - `.{prefix}-whats-new-modal` - Applied to the main content element
|
|
30
|
+
* - `.{prefix}-whats-new-modal .modal` - Modal dialog styling (max-width, width)
|
|
31
|
+
*
|
|
32
|
+
* ### Title and Subtitle
|
|
33
|
+
* - Modal title is set via `setTitle()` - Obsidian handles the styling and X close button
|
|
34
|
+
* - `.{prefix}-whats-new-subtitle` - Subtitle text ("Changes since vX.X.X")
|
|
35
|
+
*
|
|
36
|
+
* ### Support Section
|
|
37
|
+
* - `.{prefix}-whats-new-support` - Support section container
|
|
38
|
+
* - Contains donation, tools, and YouTube links
|
|
39
|
+
* - Should have background, padding, border-radius
|
|
40
|
+
* - `.{prefix}-whats-new-support h3` - Support section heading
|
|
41
|
+
* - `.{prefix}-whats-new-support p` - Support section paragraph text (one per row)
|
|
42
|
+
* - `.{prefix}-whats-new-support a` - Links in support section (consistent styling)
|
|
43
|
+
* - `.{prefix}-whats-new-support a:hover` - Link hover state
|
|
44
|
+
*
|
|
45
|
+
* ### Changelog Content
|
|
46
|
+
* - `.{prefix}-whats-new-content` - Changelog content container
|
|
47
|
+
* - Should have max-height, overflow-y: auto for scrolling
|
|
48
|
+
* - `.{prefix}-whats-new-content h2` - Version headings in changelog
|
|
49
|
+
* - `.{prefix}-whats-new-content h3` - Section headings in changelog
|
|
50
|
+
* - `.{prefix}-whats-new-content ul` - Changelog lists
|
|
51
|
+
* - `.{prefix}-whats-new-content li` - Changelog list items
|
|
52
|
+
* - `.{prefix}-whats-new-content code` - Inline code in changelog
|
|
53
|
+
* - `.{prefix}-whats-new-content pre` - Code blocks in changelog
|
|
54
|
+
* - `.{prefix}-whats-new-content a.external-link` - External links (auto-added)
|
|
55
|
+
* - `.{prefix}-whats-new-empty` - Empty state message
|
|
56
|
+
*
|
|
57
|
+
* ### Sticky Footer
|
|
58
|
+
* - `.{prefix}-whats-new-sticky-footer` - Footer container (should be sticky)
|
|
59
|
+
* - Has border-top to separate from content
|
|
60
|
+
* - `.{prefix}-whats-new-buttons` - Button container
|
|
61
|
+
* - `.{prefix}-whats-new-buttons button` - Individual buttons
|
|
62
|
+
*
|
|
63
|
+
* ## Example CSS Implementation
|
|
64
|
+
*
|
|
65
|
+
* ```css
|
|
66
|
+
* // Main Container
|
|
67
|
+
* .my-plugin-whats-new-modal .modal {
|
|
68
|
+
* max-width: 800px;
|
|
69
|
+
* width: 90%;
|
|
70
|
+
* }
|
|
71
|
+
*
|
|
72
|
+
* // Plugin Name Link (in title)
|
|
73
|
+
* .my-plugin-whats-new-plugin-name {
|
|
74
|
+
* color: var(--link-color);
|
|
75
|
+
* text-decoration: none;
|
|
76
|
+
* transition: all 0.2s ease;
|
|
77
|
+
* position: relative;
|
|
78
|
+
* font-weight: 600;
|
|
79
|
+
* }
|
|
80
|
+
*
|
|
81
|
+
* .my-plugin-whats-new-plugin-name:hover {
|
|
82
|
+
* color: var(--link-color-hover);
|
|
83
|
+
* text-decoration: none;
|
|
84
|
+
* }
|
|
85
|
+
*
|
|
86
|
+
* .my-plugin-whats-new-plugin-name::after {
|
|
87
|
+
* content: '';
|
|
88
|
+
* position: absolute;
|
|
89
|
+
* bottom: -2px;
|
|
90
|
+
* left: 0;
|
|
91
|
+
* width: 0;
|
|
92
|
+
* height: 2px;
|
|
93
|
+
* background-color: var(--interactive-accent);
|
|
94
|
+
* transition: width 0.3s ease;
|
|
95
|
+
* }
|
|
96
|
+
*
|
|
97
|
+
* .my-plugin-whats-new-plugin-name:hover::after {
|
|
98
|
+
* width: 100%;
|
|
99
|
+
* }
|
|
100
|
+
*
|
|
101
|
+
* // Subtitle
|
|
102
|
+
* .my-plugin-whats-new-subtitle {
|
|
103
|
+
* color: var(--text-muted);
|
|
104
|
+
* font-size: 0.9rem;
|
|
105
|
+
* margin: 0 0 1rem 0;
|
|
106
|
+
* }
|
|
107
|
+
*
|
|
108
|
+
* // Support Section (with donation, tools, and YouTube links)
|
|
109
|
+
* .my-plugin-whats-new-support {
|
|
110
|
+
* margin: 0 0 1rem 0;
|
|
111
|
+
* padding: 1rem;
|
|
112
|
+
* background-color: var(--background-secondary);
|
|
113
|
+
* border-radius: 8px;
|
|
114
|
+
* }
|
|
115
|
+
*
|
|
116
|
+
* .my-plugin-whats-new-support h3 {
|
|
117
|
+
* margin-top: 0;
|
|
118
|
+
* margin-bottom: 0.5rem;
|
|
119
|
+
* font-size: 1rem;
|
|
120
|
+
* }
|
|
121
|
+
*
|
|
122
|
+
* .my-plugin-whats-new-support p {
|
|
123
|
+
* margin: 0.5rem 0;
|
|
124
|
+
* color: var(--text-normal);
|
|
125
|
+
* }
|
|
126
|
+
*
|
|
127
|
+
* .my-plugin-whats-new-support a {
|
|
128
|
+
* color: var(--link-color);
|
|
129
|
+
* text-decoration: none;
|
|
130
|
+
* transition: all 0.2s ease;
|
|
131
|
+
* position: relative;
|
|
132
|
+
* }
|
|
133
|
+
*
|
|
134
|
+
* .my-plugin-whats-new-support a:hover {
|
|
135
|
+
* color: var(--link-color-hover);
|
|
136
|
+
* text-decoration: none;
|
|
137
|
+
* }
|
|
138
|
+
*
|
|
139
|
+
* .my-plugin-whats-new-support a::after {
|
|
140
|
+
* content: '';
|
|
141
|
+
* position: absolute;
|
|
142
|
+
* bottom: -2px;
|
|
143
|
+
* left: 0;
|
|
144
|
+
* width: 0;
|
|
145
|
+
* height: 2px;
|
|
146
|
+
* background-color: var(--interactive-accent);
|
|
147
|
+
* transition: width 0.3s ease;
|
|
148
|
+
* }
|
|
149
|
+
*
|
|
150
|
+
* .my-plugin-whats-new-support a:hover::after {
|
|
151
|
+
* width: 100%;
|
|
152
|
+
* }
|
|
153
|
+
*
|
|
154
|
+
* // Changelog Content (Scrollable Area)
|
|
155
|
+
* .my-plugin-whats-new-content {
|
|
156
|
+
* max-height: 400px;
|
|
157
|
+
* overflow-y: auto;
|
|
158
|
+
* margin-bottom: 1rem;
|
|
159
|
+
* padding-right: 0.5rem;
|
|
160
|
+
* border-radius: 8px;
|
|
161
|
+
* }
|
|
162
|
+
*
|
|
163
|
+
* .my-plugin-whats-new-content h2 {
|
|
164
|
+
* font-size: 1.3rem;
|
|
165
|
+
* margin-top: 1.5rem;
|
|
166
|
+
* margin-bottom: 0.5rem;
|
|
167
|
+
* color: var(--text-accent);
|
|
168
|
+
* }
|
|
169
|
+
*
|
|
170
|
+
* .my-plugin-whats-new-content h3 {
|
|
171
|
+
* font-size: 1.1rem;
|
|
172
|
+
* margin-top: 1rem;
|
|
173
|
+
* margin-bottom: 0.5rem;
|
|
174
|
+
* }
|
|
175
|
+
*
|
|
176
|
+
* .my-plugin-whats-new-content ul {
|
|
177
|
+
* padding-left: 1.5rem;
|
|
178
|
+
* }
|
|
179
|
+
*
|
|
180
|
+
* .my-plugin-whats-new-content li {
|
|
181
|
+
* margin-bottom: 0.5rem;
|
|
182
|
+
* line-height: 1.6;
|
|
183
|
+
* }
|
|
184
|
+
*
|
|
185
|
+
* .my-plugin-whats-new-content code {
|
|
186
|
+
* background: var(--code-background);
|
|
187
|
+
* padding: 0.2em 0.4em;
|
|
188
|
+
* border-radius: 3px;
|
|
189
|
+
* font-size: 0.9em;
|
|
190
|
+
* }
|
|
191
|
+
*
|
|
192
|
+
* .my-plugin-whats-new-content pre {
|
|
193
|
+
* background: var(--code-background);
|
|
194
|
+
* padding: 1rem;
|
|
195
|
+
* border-radius: 6px;
|
|
196
|
+
* overflow-x: auto;
|
|
197
|
+
* }
|
|
198
|
+
*
|
|
199
|
+
* .my-plugin-whats-new-content a.external-link {
|
|
200
|
+
* color: var(--link-external-color);
|
|
201
|
+
* }
|
|
202
|
+
*
|
|
203
|
+
* .my-plugin-whats-new-content a.external-link::after {
|
|
204
|
+
* content: "↗";
|
|
205
|
+
* margin-left: 0.2em;
|
|
206
|
+
* font-size: 0.8em;
|
|
207
|
+
* }
|
|
208
|
+
*
|
|
209
|
+
* .my-plugin-whats-new-empty {
|
|
210
|
+
* text-align: center;
|
|
211
|
+
* color: var(--text-muted);
|
|
212
|
+
* padding: 2rem;
|
|
213
|
+
* font-style: italic;
|
|
214
|
+
* }
|
|
215
|
+
*
|
|
216
|
+
* // Sticky Footer
|
|
217
|
+
* .my-plugin-whats-new-sticky-footer {
|
|
218
|
+
* position: sticky;
|
|
219
|
+
* bottom: 0;
|
|
220
|
+
* background: var(--background-primary);
|
|
221
|
+
* padding-top: 0.75rem;
|
|
222
|
+
* margin-top: 0;
|
|
223
|
+
* z-index: 10;
|
|
224
|
+
* border-top: 1px solid var(--background-modifier-border);
|
|
225
|
+
* }
|
|
226
|
+
*
|
|
227
|
+
* .my-plugin-whats-new-buttons {
|
|
228
|
+
* display: flex;
|
|
229
|
+
* gap: 0.5rem;
|
|
230
|
+
* justify-content: space-between;
|
|
231
|
+
* flex-wrap: wrap;
|
|
232
|
+
* padding-bottom: 0.5rem;
|
|
233
|
+
* width: 100%;
|
|
234
|
+
* }
|
|
235
|
+
*
|
|
236
|
+
* .my-plugin-whats-new-buttons button {
|
|
237
|
+
* flex: 1;
|
|
238
|
+
* min-width: 0;
|
|
239
|
+
* padding: 0.5rem 1rem;
|
|
240
|
+
* border-radius: 4px;
|
|
241
|
+
* cursor: pointer;
|
|
242
|
+
* border: 1px solid var(--background-modifier-border);
|
|
243
|
+
* background: var(--interactive-normal);
|
|
244
|
+
* color: var(--text-normal);
|
|
245
|
+
* transition: all 0.2s ease;
|
|
246
|
+
* text-align: center;
|
|
247
|
+
* }
|
|
248
|
+
*
|
|
249
|
+
* .my-plugin-whats-new-buttons button:hover {
|
|
250
|
+
* background: var(--interactive-hover);
|
|
251
|
+
* border-color: var(--interactive-accent);
|
|
252
|
+
* transform: translateY(-1px);
|
|
253
|
+
* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
254
|
+
* }
|
|
255
|
+
*
|
|
256
|
+
* .my-plugin-whats-new-buttons button:active {
|
|
257
|
+
* transform: translateY(0);
|
|
258
|
+
* box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
|
259
|
+
* }
|
|
260
|
+
* ```
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```typescript
|
|
264
|
+
* const modal = new WhatsNewModal(app, plugin, {
|
|
265
|
+
* cssPrefix: "my-plugin",
|
|
266
|
+
* pluginName: "My Plugin",
|
|
267
|
+
* changelogContent: rawChangelog,
|
|
268
|
+
* links: {
|
|
269
|
+
* support: "https://...",
|
|
270
|
+
* changelog: "https://...",
|
|
271
|
+
* documentation: "https://..."
|
|
272
|
+
* }
|
|
273
|
+
* }, "1.0.0", "2.0.0");
|
|
274
|
+
* modal.open();
|
|
275
|
+
* ```
|
|
22
276
|
*/
|
|
23
277
|
export class WhatsNewModal extends Modal {
|
|
24
278
|
constructor(app, plugin, config, fromVersion, toVersion) {
|
|
@@ -81,12 +335,23 @@ export class WhatsNewModal extends Modal {
|
|
|
81
335
|
const { contentEl } = this;
|
|
82
336
|
contentEl.empty();
|
|
83
337
|
this.addCls(contentEl, "whats-new-modal");
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
338
|
+
this.setTitle("");
|
|
339
|
+
const titleEl = this.titleEl;
|
|
340
|
+
titleEl.empty();
|
|
341
|
+
const pluginNameLink = titleEl.createEl("a", {
|
|
342
|
+
text: this.config.pluginName,
|
|
343
|
+
cls: this.cls("whats-new-plugin-name"),
|
|
344
|
+
href: "#",
|
|
88
345
|
});
|
|
89
|
-
|
|
346
|
+
pluginNameLink.addEventListener("click", (e) => {
|
|
347
|
+
e.preventDefault();
|
|
348
|
+
window.open(this.config.links.github, "_blank");
|
|
349
|
+
});
|
|
350
|
+
titleEl.createSpan({
|
|
351
|
+
text: ` updated to v${this.toVersion}`,
|
|
352
|
+
});
|
|
353
|
+
// Subtitle
|
|
354
|
+
contentEl.createEl("p", {
|
|
90
355
|
text: `Changes since v${this.fromVersion}`,
|
|
91
356
|
cls: this.cls("whats-new-subtitle"),
|
|
92
357
|
});
|
|
@@ -94,42 +359,29 @@ export class WhatsNewModal extends Modal {
|
|
|
94
359
|
const supportSection = contentEl.createDiv({
|
|
95
360
|
cls: this.cls("whats-new-support"),
|
|
96
361
|
});
|
|
97
|
-
supportSection.createEl("h3", { text: "Support
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
362
|
+
supportSection.createEl("h3", { text: "Support the development of this plugin" });
|
|
363
|
+
const introText = supportSection.createEl("p");
|
|
364
|
+
introText.setText("If this plugin saves you time or improves how you work in Obsidian, consider supporting its development. Your support helps fund ongoing maintenance, new features, and long-term stability.");
|
|
365
|
+
const supportLinkText = supportSection.createEl("p");
|
|
366
|
+
supportLinkText.createSpan({ text: "👉 " });
|
|
367
|
+
supportLinkText.createEl("a", {
|
|
368
|
+
text: "Support my work",
|
|
102
369
|
href: this.config.links.support,
|
|
103
370
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const discoverSection = contentEl.createDiv({
|
|
109
|
-
cls: this.cls("whats-new-discover"),
|
|
110
|
-
});
|
|
111
|
-
discoverSection.createEl("h3", { text: "Discover More" });
|
|
112
|
-
// Other tools
|
|
113
|
-
const toolsText = discoverSection.createEl("p");
|
|
114
|
-
toolsText.createSpan({ text: "🔧 Check out my " });
|
|
115
|
-
toolsText.createEl("a", {
|
|
116
|
-
text: "other plugins and productivity tools",
|
|
371
|
+
const exploreText = supportSection.createEl("p");
|
|
372
|
+
exploreText.createSpan({ text: "You can also explore my " });
|
|
373
|
+
exploreText.createEl("a", {
|
|
374
|
+
text: "other Obsidian plugins and productivity tools",
|
|
117
375
|
href: (_a = this.config.links.tools) !== null && _a !== void 0 ? _a : DEFAULT_WHATS_NEW_LINKS.TOOLS,
|
|
118
376
|
});
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
});
|
|
122
|
-
// YouTube channel
|
|
123
|
-
const youtubeText = discoverSection.createEl("p");
|
|
124
|
-
youtubeText.createSpan({ text: "📺 Subscribe to my " });
|
|
125
|
-
youtubeText.createEl("a", {
|
|
377
|
+
exploreText.createSpan({ text: ", or follow my " });
|
|
378
|
+
exploreText.createEl("a", {
|
|
126
379
|
text: "YouTube channel",
|
|
127
380
|
href: (_b = this.config.links.youtube) !== null && _b !== void 0 ? _b : DEFAULT_WHATS_NEW_LINKS.YOUTUBE,
|
|
128
381
|
});
|
|
129
|
-
|
|
130
|
-
text: " for
|
|
382
|
+
exploreText.createSpan({
|
|
383
|
+
text: " for in-depth tutorials and workflow ideas.",
|
|
131
384
|
});
|
|
132
|
-
contentEl.createEl("hr");
|
|
133
385
|
// Changelog content
|
|
134
386
|
const changelogSections = getChangelogSince(this.config.changelogContent, this.fromVersion, this.toVersion);
|
|
135
387
|
if (changelogSections.length === 0) {
|
|
@@ -147,13 +399,24 @@ export class WhatsNewModal extends Modal {
|
|
|
147
399
|
// Make external links clickable
|
|
148
400
|
this.makeExternalLinksClickable(changelogContainer);
|
|
149
401
|
}
|
|
402
|
+
// Sticky footer section (hr + buttons)
|
|
403
|
+
const stickyFooter = contentEl.createDiv({
|
|
404
|
+
cls: this.cls("whats-new-sticky-footer"),
|
|
405
|
+
});
|
|
150
406
|
// Action buttons
|
|
151
|
-
const buttonContainer =
|
|
407
|
+
const buttonContainer = stickyFooter.createDiv({
|
|
152
408
|
cls: this.cls("whats-new-buttons"),
|
|
153
409
|
});
|
|
410
|
+
// GitHub button
|
|
411
|
+
const githubBtn = buttonContainer.createEl("button", {
|
|
412
|
+
text: "GitHub",
|
|
413
|
+
});
|
|
414
|
+
githubBtn.addEventListener("click", () => {
|
|
415
|
+
window.open(this.config.links.github, "_blank");
|
|
416
|
+
});
|
|
154
417
|
// Full changelog button
|
|
155
418
|
const changelogBtn = buttonContainer.createEl("button", {
|
|
156
|
-
text: "
|
|
419
|
+
text: "Changelog",
|
|
157
420
|
});
|
|
158
421
|
changelogBtn.addEventListener("click", () => {
|
|
159
422
|
window.open(this.config.links.changelog, "_blank");
|
|
@@ -167,7 +430,7 @@ export class WhatsNewModal extends Modal {
|
|
|
167
430
|
});
|
|
168
431
|
// Tools button
|
|
169
432
|
const toolsBtn = buttonContainer.createEl("button", {
|
|
170
|
-
text: "
|
|
433
|
+
text: "Other Plugins",
|
|
171
434
|
});
|
|
172
435
|
toolsBtn.addEventListener("click", () => {
|
|
173
436
|
var _a;
|
|
@@ -175,18 +438,12 @@ export class WhatsNewModal extends Modal {
|
|
|
175
438
|
});
|
|
176
439
|
// YouTube button
|
|
177
440
|
const youtubeBtn = buttonContainer.createEl("button", {
|
|
178
|
-
text: "YouTube
|
|
441
|
+
text: "YouTube",
|
|
179
442
|
});
|
|
180
443
|
youtubeBtn.addEventListener("click", () => {
|
|
181
444
|
var _a;
|
|
182
445
|
window.open((_a = this.config.links.youtube) !== null && _a !== void 0 ? _a : DEFAULT_WHATS_NEW_LINKS.YOUTUBE, "_blank");
|
|
183
446
|
});
|
|
184
|
-
// Close button (always present)
|
|
185
|
-
const closeBtn = buttonContainer.createEl("button", {
|
|
186
|
-
text: "Close",
|
|
187
|
-
cls: this.cls("mod-cta"),
|
|
188
|
-
});
|
|
189
|
-
closeBtn.addEventListener("click", () => this.close());
|
|
190
447
|
});
|
|
191
448
|
}
|
|
192
449
|
onClose() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whats-new-modal.js","sourceRoot":"","sources":["../../src/components/whats-new-modal.ts"],"names":[],"mappings":"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","sourcesContent":["import type { App, Plugin } from \"obsidian\";\nimport { MarkdownRenderer, Modal } from \"obsidian\";\nimport { formatChangelogSections, getChangelogSince } from \"../string/changelog-parser\";\n\n/**\n * Default URLs for the What's New modal.\n * These can be overridden in the config.\n */\nexport const DEFAULT_WHATS_NEW_LINKS = {\n\t/**\n\t * Default tools page showcasing all plugins and productivity software.\n\t */\n\tTOOLS: \"https://matejvavroproductivity.com/tools/\",\n\n\t/**\n\t * Default YouTube channel with Obsidian tutorials and productivity tips.\n\t * Includes subscription confirmation parameter.\n\t */\n\tYOUTUBE: \"https://www.youtube.com/@MatejVavroProductivity?sub_confirmation=1\",\n} as const;\n\nexport interface WhatsNewModalConfig {\n\t/**\n\t * The CSS class prefix/suffix to use for styling.\n\t * Example: \"custom-calendar\" will generate classes like \"custom-calendar-whats-new-modal\"\n\t */\n\tcssPrefix: string;\n\n\t/**\n\t * Display name of the plugin.\n\t * Example: \"Custom Calendar\"\n\t */\n\tpluginName: string;\n\n\t/**\n\t * Raw changelog markdown content to parse.\n\t */\n\tchangelogContent: string;\n\n\t/**\n\t * Links to external resources.\n\t */\n\tlinks: {\n\t\t/**\n\t\t * URL to support/donate page.\n\t\t */\n\t\tsupport: string;\n\n\t\t/**\n\t\t * URL to full changelog page.\n\t\t */\n\t\tchangelog: string;\n\n\t\t/**\n\t\t * Base URL for documentation (used to resolve relative links in changelog).\n\t\t * Example: \"https://docs.example.com\" or \"https://docs.example.com/\"\n\t\t */\n\t\tdocumentation: string;\n\n\t\t/**\n\t\t * URL to tools page showcasing all plugins and productivity tools.\n\t\t * Defaults to DEFAULT_WHATS_NEW_LINKS.TOOLS if not provided.\n\t\t */\n\t\ttools?: string;\n\n\t\t/**\n\t\t * URL to YouTube channel with tutorials and productivity tips.\n\t\t * Defaults to DEFAULT_WHATS_NEW_LINKS.YOUTUBE if not provided.\n\t\t */\n\t\tyoutube?: string;\n\t};\n}\n\n/**\n * Generic \"What's New\" modal that displays changelog entries between versions.\n * Supports custom CSS prefixes, plugin names, and configurable links.\n */\nexport class WhatsNewModal extends Modal {\n\tconstructor(\n\t\tapp: App,\n\t\tprivate plugin: Plugin,\n\t\tprivate config: WhatsNewModalConfig,\n\t\tprivate fromVersion: string,\n\t\tprivate toVersion: string\n\t) {\n\t\tsuper(app);\n\t}\n\n\t/**\n\t * Helper to create CSS class names with the configured prefix.\n\t */\n\tprivate cls(suffix: string): string {\n\t\treturn `${this.config.cssPrefix}-${suffix}`;\n\t}\n\n\t/**\n\t * Helper to add CSS class to an element.\n\t */\n\tprivate addCls(el: HTMLElement, suffix: string): void {\n\t\tel.classList.add(this.cls(suffix));\n\t}\n\n\t/**\n\t * Makes external links in rendered markdown clickable by adding click handlers.\n\t * Handles both absolute URLs (http/https) and relative URLs (starting with /).\n\t * Relative URLs are resolved against the documentation base URL.\n\t */\n\tprivate makeExternalLinksClickable(container: HTMLElement): void {\n\t\tconst links = container.querySelectorAll<HTMLAnchorElement>(\"a[href]\");\n\n\t\t// Convert NodeList to Array for iteration\n\t\tArray.from(links).forEach((link) => {\n\t\t\tconst href = link.getAttribute(\"href\");\n\t\t\tif (!href) return;\n\n\t\t\tlet finalUrl: string | null = null;\n\n\t\t\t// Handle absolute HTTP(S) links\n\t\t\tif (href.startsWith(\"http://\") || href.startsWith(\"https://\")) {\n\t\t\t\tfinalUrl = href;\n\t\t\t}\n\t\t\t// Handle relative links (starting with /)\n\t\t\telse if (href.startsWith(\"/\")) {\n\t\t\t\t// Get base documentation URL and ensure proper slash handling\n\t\t\t\tconst baseUrl = this.config.links.documentation;\n\t\t\t\tconst normalizedBase = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\t\t\t\tfinalUrl = `${normalizedBase}${href}`;\n\t\t\t}\n\n\t\t\t// Add click handler for external links\n\t\t\tif (finalUrl) {\n\t\t\t\tlink.addEventListener(\"click\", (event: MouseEvent) => {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\twindow.open(finalUrl, \"_blank\");\n\t\t\t\t});\n\n\t\t\t\t// Add visual indicator that it's an external link\n\t\t\t\tlink.classList.add(\"external-link\");\n\t\t\t}\n\t\t});\n\t}\n\n\tasync onOpen() {\n\t\tconst { contentEl } = this;\n\t\tcontentEl.empty();\n\n\t\tthis.addCls(contentEl, \"whats-new-modal\");\n\n\t\t// Header section\n\t\tconst header = contentEl.createDiv({ cls: this.cls(\"whats-new-header\") });\n\t\theader.createEl(\"h2\", {\n\t\t\ttext: `${this.config.pluginName} updated to v${this.toVersion}`,\n\t\t});\n\n\t\theader.createEl(\"p\", {\n\t\t\ttext: `Changes since v${this.fromVersion}`,\n\t\t\tcls: this.cls(\"whats-new-subtitle\"),\n\t\t});\n\n\t\t// Support section\n\t\tconst supportSection = contentEl.createDiv({\n\t\t\tcls: this.cls(\"whats-new-support\"),\n\t\t});\n\n\t\tsupportSection.createEl(\"h3\", { text: \"Support My Work\" });\n\n\t\tconst supportText = supportSection.createEl(\"p\");\n\t\tsupportText.createSpan({ text: \"If you enjoy using this plugin, please consider \" });\n\t\tsupportText.createEl(\"a\", {\n\t\t\ttext: \"supporting my work\",\n\t\t\thref: this.config.links.support,\n\t\t});\n\t\tsupportText.createSpan({\n\t\t\ttext: \". Your support helps keep this plugin maintained and improved!\",\n\t\t});\n\n\t\t// Discover more section\n\t\tconst discoverSection = contentEl.createDiv({\n\t\t\tcls: this.cls(\"whats-new-discover\"),\n\t\t});\n\n\t\tdiscoverSection.createEl(\"h3\", { text: \"Discover More\" });\n\n\t\t// Other tools\n\t\tconst toolsText = discoverSection.createEl(\"p\");\n\t\ttoolsText.createSpan({ text: \"🔧 Check out my \" });\n\t\ttoolsText.createEl(\"a\", {\n\t\t\ttext: \"other plugins and productivity tools\",\n\t\t\thref: this.config.links.tools ?? DEFAULT_WHATS_NEW_LINKS.TOOLS,\n\t\t});\n\t\ttoolsText.createSpan({\n\t\t\ttext: \" to enhance your workflow even further.\",\n\t\t});\n\n\t\t// YouTube channel\n\t\tconst youtubeText = discoverSection.createEl(\"p\");\n\t\tyoutubeText.createSpan({ text: \"📺 Subscribe to my \" });\n\t\tyoutubeText.createEl(\"a\", {\n\t\t\ttext: \"YouTube channel\",\n\t\t\thref: this.config.links.youtube ?? DEFAULT_WHATS_NEW_LINKS.YOUTUBE,\n\t\t});\n\t\tyoutubeText.createSpan({\n\t\t\ttext: \" for Obsidian tutorials and productivity tips!\",\n\t\t});\n\n\t\tcontentEl.createEl(\"hr\");\n\n\t\t// Changelog content\n\t\tconst changelogSections = getChangelogSince(\n\t\t\tthis.config.changelogContent,\n\t\t\tthis.fromVersion,\n\t\t\tthis.toVersion\n\t\t);\n\n\t\tif (changelogSections.length === 0) {\n\t\t\tcontentEl.createEl(\"p\", {\n\t\t\t\ttext: \"No significant changes found in this update.\",\n\t\t\t\tcls: this.cls(\"whats-new-empty\"),\n\t\t\t});\n\t\t} else {\n\t\t\tconst changelogContainer = contentEl.createDiv({\n\t\t\t\tcls: this.cls(\"whats-new-content\"),\n\t\t\t});\n\n\t\t\tconst markdownContent = formatChangelogSections(changelogSections);\n\n\t\t\tawait MarkdownRenderer.render(\n\t\t\t\tthis.app,\n\t\t\t\tmarkdownContent,\n\t\t\t\tchangelogContainer,\n\t\t\t\t\"/\",\n\t\t\t\tthis.plugin\n\t\t\t);\n\n\t\t\t// Make external links clickable\n\t\t\tthis.makeExternalLinksClickable(changelogContainer);\n\t\t}\n\n\t\t// Action buttons\n\t\tconst buttonContainer = contentEl.createDiv({\n\t\t\tcls: this.cls(\"whats-new-buttons\"),\n\t\t});\n\n\t\t// Full changelog button\n\t\tconst changelogBtn = buttonContainer.createEl(\"button\", {\n\t\t\ttext: \"Full Changelog\",\n\t\t});\n\t\tchangelogBtn.addEventListener(\"click\", () => {\n\t\t\twindow.open(this.config.links.changelog, \"_blank\");\n\t\t});\n\n\t\t// Documentation button\n\t\tconst docsBtn = buttonContainer.createEl(\"button\", {\n\t\t\ttext: \"Documentation\",\n\t\t});\n\t\tdocsBtn.addEventListener(\"click\", () => {\n\t\t\twindow.open(this.config.links.documentation, \"_blank\");\n\t\t});\n\n\t\t// Tools button\n\t\tconst toolsBtn = buttonContainer.createEl(\"button\", {\n\t\t\ttext: \"My Tools\",\n\t\t});\n\t\ttoolsBtn.addEventListener(\"click\", () => {\n\t\t\twindow.open(this.config.links.tools ?? DEFAULT_WHATS_NEW_LINKS.TOOLS, \"_blank\");\n\t\t});\n\n\t\t// YouTube button\n\t\tconst youtubeBtn = buttonContainer.createEl(\"button\", {\n\t\t\ttext: \"YouTube Channel\",\n\t\t});\n\t\tyoutubeBtn.addEventListener(\"click\", () => {\n\t\t\twindow.open(this.config.links.youtube ?? DEFAULT_WHATS_NEW_LINKS.YOUTUBE, \"_blank\");\n\t\t});\n\n\t\t// Close button (always present)\n\t\tconst closeBtn = buttonContainer.createEl(\"button\", {\n\t\t\ttext: \"Close\",\n\t\t\tcls: this.cls(\"mod-cta\"),\n\t\t});\n\t\tcloseBtn.addEventListener(\"click\", () => this.close());\n\t}\n\n\tonClose() {\n\t\tthis.contentEl.empty();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"whats-new-modal.js","sourceRoot":"","sources":["../../src/components/whats-new-modal.ts"],"names":[],"mappings":"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","sourcesContent":["import { formatChangelogSections, getChangelogSince } from \"@real1ty-obsidian-plugins/utils\";\nimport type { App, Plugin } from \"obsidian\";\nimport { MarkdownRenderer, Modal } from \"obsidian\";\n\n/**\n * Default URLs for the What's New modal.\n * These can be overridden in the config.\n */\nexport const DEFAULT_WHATS_NEW_LINKS = {\n\t/**\n\t * Default tools page showcasing all plugins and productivity software.\n\t */\n\tTOOLS: \"https://matejvavroproductivity.com/tools/\",\n\n\t/**\n\t * Default YouTube channel with Obsidian tutorials and productivity tips.\n\t * Includes subscription confirmation parameter.\n\t */\n\tYOUTUBE: \"https://www.youtube.com/@MatejVavroProductivity?sub_confirmation=1\",\n} as const;\n\nexport interface WhatsNewModalConfig {\n\t/**\n\t * The CSS class prefix/suffix to use for styling.\n\t * Example: \"custom-calendar\" will generate classes like \"custom-calendar-whats-new-modal\"\n\t */\n\tcssPrefix: string;\n\n\t/**\n\t * Display name of the plugin.\n\t * Example: \"Custom Calendar\"\n\t */\n\tpluginName: string;\n\n\t/**\n\t * Raw changelog markdown content to parse.\n\t */\n\tchangelogContent: string;\n\n\t/**\n\t * Links to external resources.\n\t */\n\tlinks: {\n\t\t/**\n\t\t * URL to support/donate page.\n\t\t */\n\t\tsupport: string;\n\n\t\t/**\n\t\t * URL to full changelog page.\n\t\t */\n\t\tchangelog: string;\n\n\t\t/**\n\t\t * Base URL for documentation (used to resolve relative links in changelog).\n\t\t * Example: \"https://docs.example.com\" or \"https://docs.example.com/\"\n\t\t */\n\t\tdocumentation: string;\n\n\t\t/**\n\t\t * URL to GitHub repository.\n\t\t */\n\t\tgithub: string;\n\n\t\t/**\n\t\t * URL to tools page showcasing all plugins and productivity tools.\n\t\t * Defaults to DEFAULT_WHATS_NEW_LINKS.TOOLS if not provided.\n\t\t */\n\t\ttools?: string;\n\n\t\t/**\n\t\t * URL to YouTube channel with tutorials and productivity tips.\n\t\t * Defaults to DEFAULT_WHATS_NEW_LINKS.YOUTUBE if not provided.\n\t\t */\n\t\tyoutube?: string;\n\t};\n}\n\n/**\n * Generic \"What's New\" modal that displays changelog entries between versions.\n * Supports custom CSS prefixes, plugin names, and configurable links.\n *\n * ## CSS Classes\n *\n * This modal uses the following CSS classes (with your custom prefix).\n * Replace `{prefix}` with your `cssPrefix` value (e.g., \"my-plugin\").\n *\n * ### Main Container\n * - `.{prefix}-whats-new-modal` - Applied to the main content element\n * - `.{prefix}-whats-new-modal .modal` - Modal dialog styling (max-width, width)\n *\n * ### Title and Subtitle\n * - Modal title is set via `setTitle()` - Obsidian handles the styling and X close button\n * - `.{prefix}-whats-new-subtitle` - Subtitle text (\"Changes since vX.X.X\")\n *\n * ### Support Section\n * - `.{prefix}-whats-new-support` - Support section container\n * - Contains donation, tools, and YouTube links\n * - Should have background, padding, border-radius\n * - `.{prefix}-whats-new-support h3` - Support section heading\n * - `.{prefix}-whats-new-support p` - Support section paragraph text (one per row)\n * - `.{prefix}-whats-new-support a` - Links in support section (consistent styling)\n * - `.{prefix}-whats-new-support a:hover` - Link hover state\n *\n * ### Changelog Content\n * - `.{prefix}-whats-new-content` - Changelog content container\n * - Should have max-height, overflow-y: auto for scrolling\n * - `.{prefix}-whats-new-content h2` - Version headings in changelog\n * - `.{prefix}-whats-new-content h3` - Section headings in changelog\n * - `.{prefix}-whats-new-content ul` - Changelog lists\n * - `.{prefix}-whats-new-content li` - Changelog list items\n * - `.{prefix}-whats-new-content code` - Inline code in changelog\n * - `.{prefix}-whats-new-content pre` - Code blocks in changelog\n * - `.{prefix}-whats-new-content a.external-link` - External links (auto-added)\n * - `.{prefix}-whats-new-empty` - Empty state message\n *\n * ### Sticky Footer\n * - `.{prefix}-whats-new-sticky-footer` - Footer container (should be sticky)\n * - Has border-top to separate from content\n * - `.{prefix}-whats-new-buttons` - Button container\n * - `.{prefix}-whats-new-buttons button` - Individual buttons\n *\n * ## Example CSS Implementation\n *\n * ```css\n * // Main Container\n * .my-plugin-whats-new-modal .modal {\n * max-width: 800px;\n * width: 90%;\n * }\n *\n * // Plugin Name Link (in title)\n * .my-plugin-whats-new-plugin-name {\n * color: var(--link-color);\n * text-decoration: none;\n * transition: all 0.2s ease;\n * position: relative;\n * font-weight: 600;\n * }\n *\n * .my-plugin-whats-new-plugin-name:hover {\n * color: var(--link-color-hover);\n * text-decoration: none;\n * }\n *\n * .my-plugin-whats-new-plugin-name::after {\n * content: '';\n * position: absolute;\n * bottom: -2px;\n * left: 0;\n * width: 0;\n * height: 2px;\n * background-color: var(--interactive-accent);\n * transition: width 0.3s ease;\n * }\n *\n * .my-plugin-whats-new-plugin-name:hover::after {\n * width: 100%;\n * }\n *\n * // Subtitle\n * .my-plugin-whats-new-subtitle {\n * color: var(--text-muted);\n * font-size: 0.9rem;\n * margin: 0 0 1rem 0;\n * }\n *\n * // Support Section (with donation, tools, and YouTube links)\n * .my-plugin-whats-new-support {\n * margin: 0 0 1rem 0;\n * padding: 1rem;\n * background-color: var(--background-secondary);\n * border-radius: 8px;\n * }\n *\n * .my-plugin-whats-new-support h3 {\n * margin-top: 0;\n * margin-bottom: 0.5rem;\n * font-size: 1rem;\n * }\n *\n * .my-plugin-whats-new-support p {\n * margin: 0.5rem 0;\n * color: var(--text-normal);\n * }\n *\n * .my-plugin-whats-new-support a {\n * color: var(--link-color);\n * text-decoration: none;\n * transition: all 0.2s ease;\n * position: relative;\n * }\n *\n * .my-plugin-whats-new-support a:hover {\n * color: var(--link-color-hover);\n * text-decoration: none;\n * }\n *\n * .my-plugin-whats-new-support a::after {\n * content: '';\n * position: absolute;\n * bottom: -2px;\n * left: 0;\n * width: 0;\n * height: 2px;\n * background-color: var(--interactive-accent);\n * transition: width 0.3s ease;\n * }\n *\n * .my-plugin-whats-new-support a:hover::after {\n * width: 100%;\n * }\n *\n * // Changelog Content (Scrollable Area)\n * .my-plugin-whats-new-content {\n * max-height: 400px;\n * overflow-y: auto;\n * margin-bottom: 1rem;\n * padding-right: 0.5rem;\n * border-radius: 8px;\n * }\n *\n * .my-plugin-whats-new-content h2 {\n * font-size: 1.3rem;\n * margin-top: 1.5rem;\n * margin-bottom: 0.5rem;\n * color: var(--text-accent);\n * }\n *\n * .my-plugin-whats-new-content h3 {\n * font-size: 1.1rem;\n * margin-top: 1rem;\n * margin-bottom: 0.5rem;\n * }\n *\n * .my-plugin-whats-new-content ul {\n * padding-left: 1.5rem;\n * }\n *\n * .my-plugin-whats-new-content li {\n * margin-bottom: 0.5rem;\n * line-height: 1.6;\n * }\n *\n * .my-plugin-whats-new-content code {\n * background: var(--code-background);\n * padding: 0.2em 0.4em;\n * border-radius: 3px;\n * font-size: 0.9em;\n * }\n *\n * .my-plugin-whats-new-content pre {\n * background: var(--code-background);\n * padding: 1rem;\n * border-radius: 6px;\n * overflow-x: auto;\n * }\n *\n * .my-plugin-whats-new-content a.external-link {\n * color: var(--link-external-color);\n * }\n *\n * .my-plugin-whats-new-content a.external-link::after {\n * content: \"↗\";\n * margin-left: 0.2em;\n * font-size: 0.8em;\n * }\n *\n * .my-plugin-whats-new-empty {\n * text-align: center;\n * color: var(--text-muted);\n * padding: 2rem;\n * font-style: italic;\n * }\n *\n * // Sticky Footer\n * .my-plugin-whats-new-sticky-footer {\n * position: sticky;\n * bottom: 0;\n * background: var(--background-primary);\n * padding-top: 0.75rem;\n * margin-top: 0;\n * z-index: 10;\n * border-top: 1px solid var(--background-modifier-border);\n * }\n *\n * .my-plugin-whats-new-buttons {\n * display: flex;\n * gap: 0.5rem;\n * justify-content: space-between;\n * flex-wrap: wrap;\n * padding-bottom: 0.5rem;\n * width: 100%;\n * }\n *\n * .my-plugin-whats-new-buttons button {\n * flex: 1;\n * min-width: 0;\n * padding: 0.5rem 1rem;\n * border-radius: 4px;\n * cursor: pointer;\n * border: 1px solid var(--background-modifier-border);\n * background: var(--interactive-normal);\n * color: var(--text-normal);\n * transition: all 0.2s ease;\n * text-align: center;\n * }\n *\n * .my-plugin-whats-new-buttons button:hover {\n * background: var(--interactive-hover);\n * border-color: var(--interactive-accent);\n * transform: translateY(-1px);\n * box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n * }\n *\n * .my-plugin-whats-new-buttons button:active {\n * transform: translateY(0);\n * box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);\n * }\n * ```\n *\n * @example\n * ```typescript\n * const modal = new WhatsNewModal(app, plugin, {\n * cssPrefix: \"my-plugin\",\n * pluginName: \"My Plugin\",\n * changelogContent: rawChangelog,\n * links: {\n * support: \"https://...\",\n * changelog: \"https://...\",\n * documentation: \"https://...\"\n * }\n * }, \"1.0.0\", \"2.0.0\");\n * modal.open();\n * ```\n */\nexport class WhatsNewModal extends Modal {\n\tconstructor(\n\t\tapp: App,\n\t\tprivate plugin: Plugin,\n\t\tprivate config: WhatsNewModalConfig,\n\t\tprivate fromVersion: string,\n\t\tprivate toVersion: string\n\t) {\n\t\tsuper(app);\n\t}\n\n\t/**\n\t * Helper to create CSS class names with the configured prefix.\n\t */\n\tprivate cls(suffix: string): string {\n\t\treturn `${this.config.cssPrefix}-${suffix}`;\n\t}\n\n\t/**\n\t * Helper to add CSS class to an element.\n\t */\n\tprivate addCls(el: HTMLElement, suffix: string): void {\n\t\tel.classList.add(this.cls(suffix));\n\t}\n\n\t/**\n\t * Makes external links in rendered markdown clickable by adding click handlers.\n\t * Handles both absolute URLs (http/https) and relative URLs (starting with /).\n\t * Relative URLs are resolved against the documentation base URL.\n\t */\n\tprivate makeExternalLinksClickable(container: HTMLElement): void {\n\t\tconst links = container.querySelectorAll<HTMLAnchorElement>(\"a[href]\");\n\n\t\t// Convert NodeList to Array for iteration\n\t\tArray.from(links).forEach((link) => {\n\t\t\tconst href = link.getAttribute(\"href\");\n\t\t\tif (!href) return;\n\n\t\t\tlet finalUrl: string | null = null;\n\n\t\t\t// Handle absolute HTTP(S) links\n\t\t\tif (href.startsWith(\"http://\") || href.startsWith(\"https://\")) {\n\t\t\t\tfinalUrl = href;\n\t\t\t}\n\t\t\t// Handle relative links (starting with /)\n\t\t\telse if (href.startsWith(\"/\")) {\n\t\t\t\t// Get base documentation URL and ensure proper slash handling\n\t\t\t\tconst baseUrl = this.config.links.documentation;\n\t\t\t\tconst normalizedBase = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\t\t\t\tfinalUrl = `${normalizedBase}${href}`;\n\t\t\t}\n\n\t\t\t// Add click handler for external links\n\t\t\tif (finalUrl) {\n\t\t\t\tlink.addEventListener(\"click\", (event: MouseEvent) => {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\twindow.open(finalUrl, \"_blank\");\n\t\t\t\t});\n\n\t\t\t\t// Add visual indicator that it's an external link\n\t\t\t\tlink.classList.add(\"external-link\");\n\t\t\t}\n\t\t});\n\t}\n\n\tasync onOpen() {\n\t\tconst { contentEl } = this;\n\t\tcontentEl.empty();\n\n\t\tthis.addCls(contentEl, \"whats-new-modal\");\n\n\t\tthis.setTitle(\"\");\n\n\t\tconst titleEl = this.titleEl;\n\t\ttitleEl.empty();\n\n\t\tconst pluginNameLink = titleEl.createEl(\"a\", {\n\t\t\ttext: this.config.pluginName,\n\t\t\tcls: this.cls(\"whats-new-plugin-name\"),\n\t\t\thref: \"#\",\n\t\t});\n\n\t\tpluginNameLink.addEventListener(\"click\", (e) => {\n\t\t\te.preventDefault();\n\t\t\twindow.open(this.config.links.github, \"_blank\");\n\t\t});\n\n\t\ttitleEl.createSpan({\n\t\t\ttext: ` updated to v${this.toVersion}`,\n\t\t});\n\n\t\t// Subtitle\n\t\tcontentEl.createEl(\"p\", {\n\t\t\ttext: `Changes since v${this.fromVersion}`,\n\t\t\tcls: this.cls(\"whats-new-subtitle\"),\n\t\t});\n\n\t\t// Support section\n\t\tconst supportSection = contentEl.createDiv({\n\t\t\tcls: this.cls(\"whats-new-support\"),\n\t\t});\n\n\t\tsupportSection.createEl(\"h3\", { text: \"Support the development of this plugin\" });\n\n\t\tconst introText = supportSection.createEl(\"p\");\n\t\tintroText.setText(\n\t\t\t\"If this plugin saves you time or improves how you work in Obsidian, consider supporting its development. Your support helps fund ongoing maintenance, new features, and long-term stability.\"\n\t\t);\n\n\t\tconst supportLinkText = supportSection.createEl(\"p\");\n\t\tsupportLinkText.createSpan({ text: \"👉 \" });\n\t\tsupportLinkText.createEl(\"a\", {\n\t\t\ttext: \"Support my work\",\n\t\t\thref: this.config.links.support,\n\t\t});\n\n\t\tconst exploreText = supportSection.createEl(\"p\");\n\t\texploreText.createSpan({ text: \"You can also explore my \" });\n\t\texploreText.createEl(\"a\", {\n\t\t\ttext: \"other Obsidian plugins and productivity tools\",\n\t\t\thref: this.config.links.tools ?? DEFAULT_WHATS_NEW_LINKS.TOOLS,\n\t\t});\n\t\texploreText.createSpan({ text: \", or follow my \" });\n\t\texploreText.createEl(\"a\", {\n\t\t\ttext: \"YouTube channel\",\n\t\t\thref: this.config.links.youtube ?? DEFAULT_WHATS_NEW_LINKS.YOUTUBE,\n\t\t});\n\t\texploreText.createSpan({\n\t\t\ttext: \" for in-depth tutorials and workflow ideas.\",\n\t\t});\n\n\t\t// Changelog content\n\t\tconst changelogSections = getChangelogSince(\n\t\t\tthis.config.changelogContent,\n\t\t\tthis.fromVersion,\n\t\t\tthis.toVersion\n\t\t);\n\n\t\tif (changelogSections.length === 0) {\n\t\t\tcontentEl.createEl(\"p\", {\n\t\t\t\ttext: \"No significant changes found in this update.\",\n\t\t\t\tcls: this.cls(\"whats-new-empty\"),\n\t\t\t});\n\t\t} else {\n\t\t\tconst changelogContainer = contentEl.createDiv({\n\t\t\t\tcls: this.cls(\"whats-new-content\"),\n\t\t\t});\n\n\t\t\tconst markdownContent = formatChangelogSections(changelogSections);\n\n\t\t\tawait MarkdownRenderer.render(\n\t\t\t\tthis.app,\n\t\t\t\tmarkdownContent,\n\t\t\t\tchangelogContainer,\n\t\t\t\t\"/\",\n\t\t\t\tthis.plugin\n\t\t\t);\n\n\t\t\t// Make external links clickable\n\t\t\tthis.makeExternalLinksClickable(changelogContainer);\n\t\t}\n\n\t\t// Sticky footer section (hr + buttons)\n\t\tconst stickyFooter = contentEl.createDiv({\n\t\t\tcls: this.cls(\"whats-new-sticky-footer\"),\n\t\t});\n\n\t\t// Action buttons\n\t\tconst buttonContainer = stickyFooter.createDiv({\n\t\t\tcls: this.cls(\"whats-new-buttons\"),\n\t\t});\n\n\t\t// GitHub button\n\t\tconst githubBtn = buttonContainer.createEl(\"button\", {\n\t\t\ttext: \"GitHub\",\n\t\t});\n\t\tgithubBtn.addEventListener(\"click\", () => {\n\t\t\twindow.open(this.config.links.github, \"_blank\");\n\t\t});\n\n\t\t// Full changelog button\n\t\tconst changelogBtn = buttonContainer.createEl(\"button\", {\n\t\t\ttext: \"Changelog\",\n\t\t});\n\t\tchangelogBtn.addEventListener(\"click\", () => {\n\t\t\twindow.open(this.config.links.changelog, \"_blank\");\n\t\t});\n\n\t\t// Documentation button\n\t\tconst docsBtn = buttonContainer.createEl(\"button\", {\n\t\t\ttext: \"Documentation\",\n\t\t});\n\t\tdocsBtn.addEventListener(\"click\", () => {\n\t\t\twindow.open(this.config.links.documentation, \"_blank\");\n\t\t});\n\n\t\t// Tools button\n\t\tconst toolsBtn = buttonContainer.createEl(\"button\", {\n\t\t\ttext: \"Other Plugins\",\n\t\t});\n\t\ttoolsBtn.addEventListener(\"click\", () => {\n\t\t\twindow.open(this.config.links.tools ?? DEFAULT_WHATS_NEW_LINKS.TOOLS, \"_blank\");\n\t\t});\n\n\t\t// YouTube button\n\t\tconst youtubeBtn = buttonContainer.createEl(\"button\", {\n\t\t\ttext: \"YouTube\",\n\t\t});\n\t\tyoutubeBtn.addEventListener(\"click\", () => {\n\t\t\twindow.open(this.config.links.youtube ?? DEFAULT_WHATS_NEW_LINKS.YOUTUBE, \"_blank\");\n\t\t});\n\t}\n\n\tonClose() {\n\t\tthis.contentEl.empty();\n\t}\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { App, Plugin } from "obsidian";
|
|
2
2
|
import { MarkdownRenderer, Modal } from "obsidian";
|
|
3
|
-
import { formatChangelogSections, getChangelogSince } from "../string/changelog-parser";
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Default URLs for the What's New modal.
|
|
@@ -57,6 +56,11 @@ export interface WhatsNewModalConfig {
|
|
|
57
56
|
*/
|
|
58
57
|
documentation: string;
|
|
59
58
|
|
|
59
|
+
/**
|
|
60
|
+
* URL to GitHub repository.
|
|
61
|
+
*/
|
|
62
|
+
github: string;
|
|
63
|
+
|
|
60
64
|
/**
|
|
61
65
|
* URL to tools page showcasing all plugins and productivity tools.
|
|
62
66
|
* Defaults to DEFAULT_WHATS_NEW_LINKS.TOOLS if not provided.
|
|
@@ -74,6 +78,260 @@ export interface WhatsNewModalConfig {
|
|
|
74
78
|
/**
|
|
75
79
|
* Generic "What's New" modal that displays changelog entries between versions.
|
|
76
80
|
* Supports custom CSS prefixes, plugin names, and configurable links.
|
|
81
|
+
*
|
|
82
|
+
* ## CSS Classes
|
|
83
|
+
*
|
|
84
|
+
* This modal uses the following CSS classes (with your custom prefix).
|
|
85
|
+
* Replace `{prefix}` with your `cssPrefix` value (e.g., "my-plugin").
|
|
86
|
+
*
|
|
87
|
+
* ### Main Container
|
|
88
|
+
* - `.{prefix}-whats-new-modal` - Applied to the main content element
|
|
89
|
+
* - `.{prefix}-whats-new-modal .modal` - Modal dialog styling (max-width, width)
|
|
90
|
+
*
|
|
91
|
+
* ### Title and Subtitle
|
|
92
|
+
* - Modal title is set via `setTitle()` - Obsidian handles the styling and X close button
|
|
93
|
+
* - `.{prefix}-whats-new-subtitle` - Subtitle text ("Changes since vX.X.X")
|
|
94
|
+
*
|
|
95
|
+
* ### Support Section
|
|
96
|
+
* - `.{prefix}-whats-new-support` - Support section container
|
|
97
|
+
* - Contains donation, tools, and YouTube links
|
|
98
|
+
* - Should have background, padding, border-radius
|
|
99
|
+
* - `.{prefix}-whats-new-support h3` - Support section heading
|
|
100
|
+
* - `.{prefix}-whats-new-support p` - Support section paragraph text (one per row)
|
|
101
|
+
* - `.{prefix}-whats-new-support a` - Links in support section (consistent styling)
|
|
102
|
+
* - `.{prefix}-whats-new-support a:hover` - Link hover state
|
|
103
|
+
*
|
|
104
|
+
* ### Changelog Content
|
|
105
|
+
* - `.{prefix}-whats-new-content` - Changelog content container
|
|
106
|
+
* - Should have max-height, overflow-y: auto for scrolling
|
|
107
|
+
* - `.{prefix}-whats-new-content h2` - Version headings in changelog
|
|
108
|
+
* - `.{prefix}-whats-new-content h3` - Section headings in changelog
|
|
109
|
+
* - `.{prefix}-whats-new-content ul` - Changelog lists
|
|
110
|
+
* - `.{prefix}-whats-new-content li` - Changelog list items
|
|
111
|
+
* - `.{prefix}-whats-new-content code` - Inline code in changelog
|
|
112
|
+
* - `.{prefix}-whats-new-content pre` - Code blocks in changelog
|
|
113
|
+
* - `.{prefix}-whats-new-content a.external-link` - External links (auto-added)
|
|
114
|
+
* - `.{prefix}-whats-new-empty` - Empty state message
|
|
115
|
+
*
|
|
116
|
+
* ### Sticky Footer
|
|
117
|
+
* - `.{prefix}-whats-new-sticky-footer` - Footer container (should be sticky)
|
|
118
|
+
* - Has border-top to separate from content
|
|
119
|
+
* - `.{prefix}-whats-new-buttons` - Button container
|
|
120
|
+
* - `.{prefix}-whats-new-buttons button` - Individual buttons
|
|
121
|
+
*
|
|
122
|
+
* ## Example CSS Implementation
|
|
123
|
+
*
|
|
124
|
+
* ```css
|
|
125
|
+
* // Main Container
|
|
126
|
+
* .my-plugin-whats-new-modal .modal {
|
|
127
|
+
* max-width: 800px;
|
|
128
|
+
* width: 90%;
|
|
129
|
+
* }
|
|
130
|
+
*
|
|
131
|
+
* // Plugin Name Link (in title)
|
|
132
|
+
* .my-plugin-whats-new-plugin-name {
|
|
133
|
+
* color: var(--link-color);
|
|
134
|
+
* text-decoration: none;
|
|
135
|
+
* transition: all 0.2s ease;
|
|
136
|
+
* position: relative;
|
|
137
|
+
* font-weight: 600;
|
|
138
|
+
* }
|
|
139
|
+
*
|
|
140
|
+
* .my-plugin-whats-new-plugin-name:hover {
|
|
141
|
+
* color: var(--link-color-hover);
|
|
142
|
+
* text-decoration: none;
|
|
143
|
+
* }
|
|
144
|
+
*
|
|
145
|
+
* .my-plugin-whats-new-plugin-name::after {
|
|
146
|
+
* content: '';
|
|
147
|
+
* position: absolute;
|
|
148
|
+
* bottom: -2px;
|
|
149
|
+
* left: 0;
|
|
150
|
+
* width: 0;
|
|
151
|
+
* height: 2px;
|
|
152
|
+
* background-color: var(--interactive-accent);
|
|
153
|
+
* transition: width 0.3s ease;
|
|
154
|
+
* }
|
|
155
|
+
*
|
|
156
|
+
* .my-plugin-whats-new-plugin-name:hover::after {
|
|
157
|
+
* width: 100%;
|
|
158
|
+
* }
|
|
159
|
+
*
|
|
160
|
+
* // Subtitle
|
|
161
|
+
* .my-plugin-whats-new-subtitle {
|
|
162
|
+
* color: var(--text-muted);
|
|
163
|
+
* font-size: 0.9rem;
|
|
164
|
+
* margin: 0 0 1rem 0;
|
|
165
|
+
* }
|
|
166
|
+
*
|
|
167
|
+
* // Support Section (with donation, tools, and YouTube links)
|
|
168
|
+
* .my-plugin-whats-new-support {
|
|
169
|
+
* margin: 0 0 1rem 0;
|
|
170
|
+
* padding: 1rem;
|
|
171
|
+
* background-color: var(--background-secondary);
|
|
172
|
+
* border-radius: 8px;
|
|
173
|
+
* }
|
|
174
|
+
*
|
|
175
|
+
* .my-plugin-whats-new-support h3 {
|
|
176
|
+
* margin-top: 0;
|
|
177
|
+
* margin-bottom: 0.5rem;
|
|
178
|
+
* font-size: 1rem;
|
|
179
|
+
* }
|
|
180
|
+
*
|
|
181
|
+
* .my-plugin-whats-new-support p {
|
|
182
|
+
* margin: 0.5rem 0;
|
|
183
|
+
* color: var(--text-normal);
|
|
184
|
+
* }
|
|
185
|
+
*
|
|
186
|
+
* .my-plugin-whats-new-support a {
|
|
187
|
+
* color: var(--link-color);
|
|
188
|
+
* text-decoration: none;
|
|
189
|
+
* transition: all 0.2s ease;
|
|
190
|
+
* position: relative;
|
|
191
|
+
* }
|
|
192
|
+
*
|
|
193
|
+
* .my-plugin-whats-new-support a:hover {
|
|
194
|
+
* color: var(--link-color-hover);
|
|
195
|
+
* text-decoration: none;
|
|
196
|
+
* }
|
|
197
|
+
*
|
|
198
|
+
* .my-plugin-whats-new-support a::after {
|
|
199
|
+
* content: '';
|
|
200
|
+
* position: absolute;
|
|
201
|
+
* bottom: -2px;
|
|
202
|
+
* left: 0;
|
|
203
|
+
* width: 0;
|
|
204
|
+
* height: 2px;
|
|
205
|
+
* background-color: var(--interactive-accent);
|
|
206
|
+
* transition: width 0.3s ease;
|
|
207
|
+
* }
|
|
208
|
+
*
|
|
209
|
+
* .my-plugin-whats-new-support a:hover::after {
|
|
210
|
+
* width: 100%;
|
|
211
|
+
* }
|
|
212
|
+
*
|
|
213
|
+
* // Changelog Content (Scrollable Area)
|
|
214
|
+
* .my-plugin-whats-new-content {
|
|
215
|
+
* max-height: 400px;
|
|
216
|
+
* overflow-y: auto;
|
|
217
|
+
* margin-bottom: 1rem;
|
|
218
|
+
* padding-right: 0.5rem;
|
|
219
|
+
* border-radius: 8px;
|
|
220
|
+
* }
|
|
221
|
+
*
|
|
222
|
+
* .my-plugin-whats-new-content h2 {
|
|
223
|
+
* font-size: 1.3rem;
|
|
224
|
+
* margin-top: 1.5rem;
|
|
225
|
+
* margin-bottom: 0.5rem;
|
|
226
|
+
* color: var(--text-accent);
|
|
227
|
+
* }
|
|
228
|
+
*
|
|
229
|
+
* .my-plugin-whats-new-content h3 {
|
|
230
|
+
* font-size: 1.1rem;
|
|
231
|
+
* margin-top: 1rem;
|
|
232
|
+
* margin-bottom: 0.5rem;
|
|
233
|
+
* }
|
|
234
|
+
*
|
|
235
|
+
* .my-plugin-whats-new-content ul {
|
|
236
|
+
* padding-left: 1.5rem;
|
|
237
|
+
* }
|
|
238
|
+
*
|
|
239
|
+
* .my-plugin-whats-new-content li {
|
|
240
|
+
* margin-bottom: 0.5rem;
|
|
241
|
+
* line-height: 1.6;
|
|
242
|
+
* }
|
|
243
|
+
*
|
|
244
|
+
* .my-plugin-whats-new-content code {
|
|
245
|
+
* background: var(--code-background);
|
|
246
|
+
* padding: 0.2em 0.4em;
|
|
247
|
+
* border-radius: 3px;
|
|
248
|
+
* font-size: 0.9em;
|
|
249
|
+
* }
|
|
250
|
+
*
|
|
251
|
+
* .my-plugin-whats-new-content pre {
|
|
252
|
+
* background: var(--code-background);
|
|
253
|
+
* padding: 1rem;
|
|
254
|
+
* border-radius: 6px;
|
|
255
|
+
* overflow-x: auto;
|
|
256
|
+
* }
|
|
257
|
+
*
|
|
258
|
+
* .my-plugin-whats-new-content a.external-link {
|
|
259
|
+
* color: var(--link-external-color);
|
|
260
|
+
* }
|
|
261
|
+
*
|
|
262
|
+
* .my-plugin-whats-new-content a.external-link::after {
|
|
263
|
+
* content: "↗";
|
|
264
|
+
* margin-left: 0.2em;
|
|
265
|
+
* font-size: 0.8em;
|
|
266
|
+
* }
|
|
267
|
+
*
|
|
268
|
+
* .my-plugin-whats-new-empty {
|
|
269
|
+
* text-align: center;
|
|
270
|
+
* color: var(--text-muted);
|
|
271
|
+
* padding: 2rem;
|
|
272
|
+
* font-style: italic;
|
|
273
|
+
* }
|
|
274
|
+
*
|
|
275
|
+
* // Sticky Footer
|
|
276
|
+
* .my-plugin-whats-new-sticky-footer {
|
|
277
|
+
* position: sticky;
|
|
278
|
+
* bottom: 0;
|
|
279
|
+
* background: var(--background-primary);
|
|
280
|
+
* padding-top: 0.75rem;
|
|
281
|
+
* margin-top: 0;
|
|
282
|
+
* z-index: 10;
|
|
283
|
+
* border-top: 1px solid var(--background-modifier-border);
|
|
284
|
+
* }
|
|
285
|
+
*
|
|
286
|
+
* .my-plugin-whats-new-buttons {
|
|
287
|
+
* display: flex;
|
|
288
|
+
* gap: 0.5rem;
|
|
289
|
+
* justify-content: space-between;
|
|
290
|
+
* flex-wrap: wrap;
|
|
291
|
+
* padding-bottom: 0.5rem;
|
|
292
|
+
* width: 100%;
|
|
293
|
+
* }
|
|
294
|
+
*
|
|
295
|
+
* .my-plugin-whats-new-buttons button {
|
|
296
|
+
* flex: 1;
|
|
297
|
+
* min-width: 0;
|
|
298
|
+
* padding: 0.5rem 1rem;
|
|
299
|
+
* border-radius: 4px;
|
|
300
|
+
* cursor: pointer;
|
|
301
|
+
* border: 1px solid var(--background-modifier-border);
|
|
302
|
+
* background: var(--interactive-normal);
|
|
303
|
+
* color: var(--text-normal);
|
|
304
|
+
* transition: all 0.2s ease;
|
|
305
|
+
* text-align: center;
|
|
306
|
+
* }
|
|
307
|
+
*
|
|
308
|
+
* .my-plugin-whats-new-buttons button:hover {
|
|
309
|
+
* background: var(--interactive-hover);
|
|
310
|
+
* border-color: var(--interactive-accent);
|
|
311
|
+
* transform: translateY(-1px);
|
|
312
|
+
* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
313
|
+
* }
|
|
314
|
+
*
|
|
315
|
+
* .my-plugin-whats-new-buttons button:active {
|
|
316
|
+
* transform: translateY(0);
|
|
317
|
+
* box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
|
318
|
+
* }
|
|
319
|
+
* ```
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* ```typescript
|
|
323
|
+
* const modal = new WhatsNewModal(app, plugin, {
|
|
324
|
+
* cssPrefix: "my-plugin",
|
|
325
|
+
* pluginName: "My Plugin",
|
|
326
|
+
* changelogContent: rawChangelog,
|
|
327
|
+
* links: {
|
|
328
|
+
* support: "https://...",
|
|
329
|
+
* changelog: "https://...",
|
|
330
|
+
* documentation: "https://..."
|
|
331
|
+
* }
|
|
332
|
+
* }, "1.0.0", "2.0.0");
|
|
333
|
+
* modal.open();
|
|
334
|
+
* ```
|
|
77
335
|
*/
|
|
78
336
|
export class WhatsNewModal extends Modal {
|
|
79
337
|
constructor(
|
|
@@ -146,13 +404,28 @@ export class WhatsNewModal extends Modal {
|
|
|
146
404
|
|
|
147
405
|
this.addCls(contentEl, "whats-new-modal");
|
|
148
406
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
407
|
+
this.setTitle("");
|
|
408
|
+
|
|
409
|
+
const titleEl = this.titleEl;
|
|
410
|
+
titleEl.empty();
|
|
411
|
+
|
|
412
|
+
const pluginNameLink = titleEl.createEl("a", {
|
|
413
|
+
text: this.config.pluginName,
|
|
414
|
+
cls: this.cls("whats-new-plugin-name"),
|
|
415
|
+
href: "#",
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
pluginNameLink.addEventListener("click", (e) => {
|
|
419
|
+
e.preventDefault();
|
|
420
|
+
window.open(this.config.links.github, "_blank");
|
|
153
421
|
});
|
|
154
422
|
|
|
155
|
-
|
|
423
|
+
titleEl.createSpan({
|
|
424
|
+
text: ` updated to v${this.toVersion}`,
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// Subtitle
|
|
428
|
+
contentEl.createEl("p", {
|
|
156
429
|
text: `Changes since v${this.fromVersion}`,
|
|
157
430
|
cls: this.cls("whats-new-subtitle"),
|
|
158
431
|
});
|
|
@@ -162,49 +435,35 @@ export class WhatsNewModal extends Modal {
|
|
|
162
435
|
cls: this.cls("whats-new-support"),
|
|
163
436
|
});
|
|
164
437
|
|
|
165
|
-
supportSection.createEl("h3", { text: "Support
|
|
438
|
+
supportSection.createEl("h3", { text: "Support the development of this plugin" });
|
|
166
439
|
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
href: this.config.links.support,
|
|
172
|
-
});
|
|
173
|
-
supportText.createSpan({
|
|
174
|
-
text: ". Your support helps keep this plugin maintained and improved!",
|
|
175
|
-
});
|
|
440
|
+
const introText = supportSection.createEl("p");
|
|
441
|
+
introText.setText(
|
|
442
|
+
"If this plugin saves you time or improves how you work in Obsidian, consider supporting its development. Your support helps fund ongoing maintenance, new features, and long-term stability."
|
|
443
|
+
);
|
|
176
444
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
445
|
+
const supportLinkText = supportSection.createEl("p");
|
|
446
|
+
supportLinkText.createSpan({ text: "👉 " });
|
|
447
|
+
supportLinkText.createEl("a", {
|
|
448
|
+
text: "Support my work",
|
|
449
|
+
href: this.config.links.support,
|
|
180
450
|
});
|
|
181
451
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
toolsText.createSpan({ text: "🔧 Check out my " });
|
|
187
|
-
toolsText.createEl("a", {
|
|
188
|
-
text: "other plugins and productivity tools",
|
|
452
|
+
const exploreText = supportSection.createEl("p");
|
|
453
|
+
exploreText.createSpan({ text: "You can also explore my " });
|
|
454
|
+
exploreText.createEl("a", {
|
|
455
|
+
text: "other Obsidian plugins and productivity tools",
|
|
189
456
|
href: this.config.links.tools ?? DEFAULT_WHATS_NEW_LINKS.TOOLS,
|
|
190
457
|
});
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
// YouTube channel
|
|
196
|
-
const youtubeText = discoverSection.createEl("p");
|
|
197
|
-
youtubeText.createSpan({ text: "📺 Subscribe to my " });
|
|
198
|
-
youtubeText.createEl("a", {
|
|
458
|
+
exploreText.createSpan({ text: ", or follow my " });
|
|
459
|
+
exploreText.createEl("a", {
|
|
199
460
|
text: "YouTube channel",
|
|
200
461
|
href: this.config.links.youtube ?? DEFAULT_WHATS_NEW_LINKS.YOUTUBE,
|
|
201
462
|
});
|
|
202
|
-
|
|
203
|
-
text: " for
|
|
463
|
+
exploreText.createSpan({
|
|
464
|
+
text: " for in-depth tutorials and workflow ideas.",
|
|
204
465
|
});
|
|
205
466
|
|
|
206
|
-
contentEl.createEl("hr");
|
|
207
|
-
|
|
208
467
|
// Changelog content
|
|
209
468
|
const changelogSections = getChangelogSince(
|
|
210
469
|
this.config.changelogContent,
|
|
@@ -236,14 +495,27 @@ export class WhatsNewModal extends Modal {
|
|
|
236
495
|
this.makeExternalLinksClickable(changelogContainer);
|
|
237
496
|
}
|
|
238
497
|
|
|
498
|
+
// Sticky footer section (hr + buttons)
|
|
499
|
+
const stickyFooter = contentEl.createDiv({
|
|
500
|
+
cls: this.cls("whats-new-sticky-footer"),
|
|
501
|
+
});
|
|
502
|
+
|
|
239
503
|
// Action buttons
|
|
240
|
-
const buttonContainer =
|
|
504
|
+
const buttonContainer = stickyFooter.createDiv({
|
|
241
505
|
cls: this.cls("whats-new-buttons"),
|
|
242
506
|
});
|
|
243
507
|
|
|
508
|
+
// GitHub button
|
|
509
|
+
const githubBtn = buttonContainer.createEl("button", {
|
|
510
|
+
text: "GitHub",
|
|
511
|
+
});
|
|
512
|
+
githubBtn.addEventListener("click", () => {
|
|
513
|
+
window.open(this.config.links.github, "_blank");
|
|
514
|
+
});
|
|
515
|
+
|
|
244
516
|
// Full changelog button
|
|
245
517
|
const changelogBtn = buttonContainer.createEl("button", {
|
|
246
|
-
text: "
|
|
518
|
+
text: "Changelog",
|
|
247
519
|
});
|
|
248
520
|
changelogBtn.addEventListener("click", () => {
|
|
249
521
|
window.open(this.config.links.changelog, "_blank");
|
|
@@ -259,7 +531,7 @@ export class WhatsNewModal extends Modal {
|
|
|
259
531
|
|
|
260
532
|
// Tools button
|
|
261
533
|
const toolsBtn = buttonContainer.createEl("button", {
|
|
262
|
-
text: "
|
|
534
|
+
text: "Other Plugins",
|
|
263
535
|
});
|
|
264
536
|
toolsBtn.addEventListener("click", () => {
|
|
265
537
|
window.open(this.config.links.tools ?? DEFAULT_WHATS_NEW_LINKS.TOOLS, "_blank");
|
|
@@ -267,18 +539,11 @@ export class WhatsNewModal extends Modal {
|
|
|
267
539
|
|
|
268
540
|
// YouTube button
|
|
269
541
|
const youtubeBtn = buttonContainer.createEl("button", {
|
|
270
|
-
text: "YouTube
|
|
542
|
+
text: "YouTube",
|
|
271
543
|
});
|
|
272
544
|
youtubeBtn.addEventListener("click", () => {
|
|
273
545
|
window.open(this.config.links.youtube ?? DEFAULT_WHATS_NEW_LINKS.YOUTUBE, "_blank");
|
|
274
546
|
});
|
|
275
|
-
|
|
276
|
-
// Close button (always present)
|
|
277
|
-
const closeBtn = buttonContainer.createEl("button", {
|
|
278
|
-
text: "Close",
|
|
279
|
-
cls: this.cls("mod-cta"),
|
|
280
|
-
});
|
|
281
|
-
closeBtn.addEventListener("click", () => this.close());
|
|
282
547
|
}
|
|
283
548
|
|
|
284
549
|
onClose() {
|