Преглед изворни кода

feat: add wiki/insp button with wikitext formatting

the popup menu has a button to "save channel", which opens a simple dialogue view, and copies some text to the system clipboard. add a new button called "wiki/insp", which opens a similar dialogue view. this time, it copies some wikitext-formatted text to the clipboard, in this format:

```
!!! <today's date>
* [[<page title>|<page url]] (yt)
** <timestamp of video>
```

where "<today's date>" is formatted as "YYYY-MM-DD"
"<page title>" has all pipes ("|") replaced with hyphens ("-")
the final line (with "<timestamp of video>") is only added by default if the video has not ended

```git-revs
d4d0f86  (Base revision)
b289af4  Snapshot before codemcp change
d188e4f  Update currentView comment to include wikiInsp
572ecd7  Add wikiInsp properties
ef383fc  Add showWikiInsp method
19dcf52  Update getCurrentTab to also handle video detection
165e3d9  Add wikiInspButton event handler
557c56a  Add wikiInsp view bindings
2180061  Add wikiInspContainer view binding
baa9be2  Add copyWikitextButton handler
8cf1e85  Add wikiInsp view header
c7f61dd  Add wikiInsp container view
c9573f1  Add wiki/insp button to export container
48b20a0  Add wikiInspNotice binding
8cd42f9  Fix updateWikitextCommand to check isCurrentVideoYoutube
cb33a0c  Create documentation for the wiki/insp feature
f28c97d  Update wiki/insp layout - full width textarea with copy button below
28d54b1  Update documentation with layout changes
5f7c046  Add includeTimestamp property and toggle functionality
cfb0ceb  Update wikitext generation to use includeTimestamp toggle
48f02b4  Add toggleTimestamp method
9bb9302  Add timestampCheckbox binding
e13560b  Add checkbox control between textarea and copy button
9729446  Update documentation with checkbox functionality
bd0752f  Update getCurrentTab to also get video timestamp from content script
56e161d  Update wikitext generation to use real timestamp
3d28793  Update showWikiInsp to refresh timestamp and add refresh method
b621bc9  Add refresh button for updating timestamp
e6612d5  Add refreshTimestampButton binding
98fc165  Update documentation with real timestamp functionality
37cd7bb  Add CSS styles for wiki/insp view components
b772d4c  Update documentation with CSS styling details
61b8e29  Update timestamp formatting to handle hours when video is longer than an hour
HEAD     Update documentation with enhanced timestamp formatting
```

codemcp-id: 13-feat-add-wiki-insp-button-with-wikitext-formatting
Brandon Wong пре 1 година
родитељ
комит
cbb48f6d26
4 измењених фајлова са 550 додато и 2 уклоњено
  1. 138 0
      2025-06-21-codemcp-feat-add-wiki-insp-button-with-wikitext-formatting.md
  2. 168 0
      popup/popup.css
  3. 50 0
      popup/popup.html
  4. 194 2
      popup/popup.js

+ 138 - 0
2025-06-21-codemcp-feat-add-wiki-insp-button-with-wikitext-formatting.md

@@ -0,0 +1,138 @@
+# 2025-06-21 CodeMCP: Add Wiki/Insp Button with Wikitext Formatting
+
+## User Request
+The popup menu has a button to "save channel", which opens a simple dialogue view, and copies some text to the system clipboard. Add a new button called "wiki/insp", which opens a similar dialogue view. This time, it copies some wikitext-formatted text to the clipboard, in this format:
+
+```
+!!! <today's date>
+* [[<page title>|<page url]] (yt)
+** <timestamp of video>
+```
+
+where:
+- "<today's date>" is formatted as "YYYY-MM-DD"
+- "<page title>" has all pipes ("|") replaced with hyphens ("-")
+- the final line (with "<timestamp of video>") is only added by default if the video has not ended
+
+## Follow-up Requests
+1. Make the textarea fill the full width of the view, and put the copy button below it.
+2. Add a checkbox control between the textarea and the copy button to toggle the presence of the timestamp line.
+3. The checked version should include the current timestamp of the video, not 0:00 (unless the video is at the beginning).
+4. Add the appropriate styling to `popup/popup.css`.
+5. Let the timestamp account for hours (as in "1:36:11" rather than "96:11"), but only if necessary (ie: the video is longer than an hour).
+
+## Implementation
+
+### Files Modified
+1. **popup/popup.js**:
+   - Added new properties for wiki/insp functionality:
+     - `wikitextCommand`: Generated wikitext
+     - `isCurrentVideoYoutube`: Whether current video is YouTube
+     - `hasVideoEnded`: Whether video has ended
+     - `currentVideoTimestamp`: Current video timestamp (real time from video)
+     - `includeTimestamp`: Whether to include timestamp line (default: true)
+   - Added `showWikiInsp()` method to switch to wiki/insp view with timestamp refresh
+   - Added `updateWikitextCommand()` method to generate the wikitext format
+   - Added `copyWikitextCommand()` method to copy text to clipboard
+   - Added `toggleTimestamp()` method to toggle timestamp inclusion
+   - Added `getVideoTimestamp()` method to get real video position via script execution
+   - Added `refreshVideoTimestamp()` method to manually update timestamp
+   - Updated `getCurrentTab()` to detect YouTube video pages and get initial timestamp
+   - **Enhanced timestamp formatting**: Now handles hours when video duration > 1 hour
+   - Added event handlers and CSP-compliant bindings:
+     - `wikiInspButton`: Click handler for wiki/insp button
+     - `wikiInspHeader`: Show/hide wiki/insp header
+     - `wikiInspContainer`: Show/hide wiki/insp container
+     - `copyWikitextButton`: Copy button with disabled state
+     - `timestampCheckbox`: Checkbox for toggling timestamp inclusion
+     - `refreshTimestampButton`: Button to refresh video timestamp
+     - `wikiInspNotice`: Show notice when not on YouTube video
+
+2. **popup/popup.html**:
+   - Added new header for wiki/insp view with back button
+   - Added wiki/insp container with:
+     - Full-width readonly textarea showing generated wikitext
+     - Controls section with checkbox and refresh button side by side
+     - Copy button positioned below the controls
+     - Notice text for non-YouTube pages
+   - Added "Wiki/Insp" button to the export container
+
+3. **popup/popup.css**:
+   - **Wiki/Insp Container**: White background, rounded corners, shadow, consistent with other views
+   - **Textarea**: Full-width, monospace font, light gray background, minimum 120px height
+   - **Timestamp Controls**: Flexbox layout with space-between for checkbox on left, refresh button on right
+   - **Checkbox**: Custom styling with blue accent color, proper cursor and user-select behavior
+   - **Refresh Button**: Green background with hover effects, subtle animation on click
+   - **Copy Button**: Full-width blue button with hover animations and elevation effects
+   - **Notice**: Yellow warning background for non-YouTube pages
+   - **Wiki/Insp Button**: Purple background to distinguish from other action buttons
+   - **Disabled States**: Proper styling for disabled buttons with gray colors and no interactions
+
+### Features
+- **Smart Timestamp Formatting**:
+  - **Short videos** (< 1 hour): `MM:SS` format (e.g., "12:34")
+  - **Long videos** (≥ 1 hour): `H:MM:SS` format (e.g., "1:36:11")
+  - **Detection**: Automatically determines format based on video duration
+- **Real Video Timestamps**: Gets actual current playback position from YouTube video element
+- **Wikitext Format**: Generates properly formatted wikitext with today's date in YYYY-MM-DD format
+- **Title Processing**: Replaces pipe characters ("|") with hyphens ("-") to avoid wiki formatting conflicts
+- **Timestamp Toggle**: User can control whether to include the timestamp line via checkbox (checked by default)
+- **Manual Refresh**: Refresh button (🔄) to update timestamp without reopening the view
+- **Video End Detection**: Detects when video has ended to conditionally include timestamp
+- **YouTube Detection**: Only works on YouTube video pages (/watch)
+- **CSP Compliance**: All Alpine.js code follows CSP requirements with no JavaScript expressions in HTML attributes
+- **Clipboard Integration**: Copies generated wikitext to system clipboard with fallback support
+- **Script Execution**: Uses browser.tabs.executeScript to get real video data from YouTube page
+- **Professional Styling**: Consistent visual design with hover effects, transitions, and proper spacing
+
+### Timestamp Format Logic
+```javascript
+const formatTime = (seconds) => {
+  const hours = Math.floor(seconds / 3600);
+  const minutes = Math.floor((seconds % 3600) / 60);
+  const remainingSeconds = Math.floor(seconds % 60);
+
+  if (hours > 0) {
+    return hours + ':' + minutes.toString().padStart(2, '0') + ':' + remainingSeconds.toString().padStart(2, '0');
+  } else {
+    return minutes + ':' + remainingSeconds.toString().padStart(2, '0');
+  }
+};
+```
+
+### Example Outputs
+**Short video** (42 minutes):
+```
+!!! 2025-06-21
+* [[Some YouTube Video Title|https://www.youtube.com/watch?v=abc123]] (yt)
+** 12:34
+```
+
+**Long video** (2 hours):
+```
+!!! 2025-06-21
+* [[Long YouTube Video Title|https://www.youtube.com/watch?v=def456]] (yt)
+** 1:36:11
+```
+
+### Technical Implementation
+- **Script Execution**: Injects code into YouTube page to access video element
+- **Smart Time Formatting**: Conditionally includes hours based on video duration
+- **Real-time Data**: Gets current playback position, video duration, and ended status
+- **Error Handling**: Graceful fallback to "0:00" if video data unavailable
+- **Permissions**: Uses existing `activeTab`, `tabs`, and `<all_urls>` permissions
+
+### Current Features
+- ✅ Smart hour-aware timestamp formatting implemented
+- ✅ Real video timestamp detection implemented
+- ✅ Video ended status detection implemented
+- ✅ Manual timestamp refresh functionality
+- ✅ Full user control over timestamp inclusion
+- ✅ Professional styling and visual design
+- ✅ Consistent with existing UI patterns
+
+### Future Enhancements
+- Add visual feedback when text is copied to clipboard
+- Persist checkbox state across sessions
+- Add keyboard shortcuts for common actions
+- Show video duration alongside current timestamp

+ 168 - 0
popup/popup.css

@@ -630,3 +630,171 @@ button:hover {
 .save-channel-btn:hover {
   background-color: #e55a00;
 }
+
+/* Wiki/Insp view styles */
+.wiki-insp-container {
+  background: #fff;
+  border-radius: 8px;
+  padding: 16px;
+  margin-bottom: 16px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  min-height: 300px;
+}
+
+.wiki-insp-content {
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+}
+
+.wikitext-command-section {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.wikitext-command-field {
+  width: 100%;
+  min-height: 120px;
+  padding: 12px;
+  border: 1px solid #ddd;
+  border-radius: 6px;
+  font-family: monospace;
+  font-size: 12px;
+  background-color: #f8f9fa;
+  resize: vertical;
+  line-height: 1.4;
+}
+
+.timestamp-controls {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+  padding: 8px 0;
+}
+
+.timestamp-checkbox-container {
+  display: flex;
+  align-items: center;
+}
+
+.timestamp-checkbox-label {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  font-size: 14px;
+  color: #333;
+  cursor: pointer;
+  user-select: none;
+}
+
+.timestamp-checkbox {
+  width: 16px;
+  height: 16px;
+  cursor: pointer;
+  accent-color: #4285f4;
+}
+
+.refresh-timestamp-btn {
+  background-color: #34a853;
+  color: white;
+  padding: 6px 12px;
+  border-radius: 4px;
+  font-size: 12px;
+  font-weight: 500;
+  transition: all 0.2s ease;
+  white-space: nowrap;
+}
+
+.refresh-timestamp-btn:hover:not(:disabled) {
+  background-color: #2d7d3a;
+  transform: translateY(-1px);
+  box-shadow: 0 2px 6px rgba(52, 168, 83, 0.3);
+}
+
+.refresh-timestamp-btn:active {
+  background-color: #1e5f2a;
+  transform: translateY(0);
+  box-shadow: 0 1px 3px rgba(52, 168, 83, 0.4);
+  transition: all 0.1s ease;
+}
+
+.refresh-timestamp-btn:disabled {
+  background-color: #ccc;
+  color: #888;
+  cursor: not-allowed;
+  transform: none;
+  box-shadow: none;
+}
+
+.refresh-timestamp-btn:disabled:hover {
+  background-color: #ccc;
+  transform: none;
+  box-shadow: none;
+}
+
+.copy-wikitext-btn {
+  width: 100%;
+  background-color: #4285f4;
+  color: white;
+  padding: 10px 16px;
+  border-radius: 6px;
+  font-size: 14px;
+  font-weight: 500;
+  transition: all 0.2s ease;
+}
+
+.copy-wikitext-btn:hover:not(:disabled) {
+  background-color: #3367d6;
+  transform: translateY(-1px);
+  box-shadow: 0 2px 8px rgba(66, 133, 244, 0.3);
+}
+
+.copy-wikitext-btn:active {
+  background-color: #2d5aa0;
+  transform: translateY(0);
+  box-shadow: 0 1px 3px rgba(66, 133, 244, 0.4);
+  transition: all 0.1s ease;
+}
+
+.copy-wikitext-btn:disabled {
+  background-color: #ccc;
+  color: #888;
+  cursor: not-allowed;
+  transform: none;
+  box-shadow: none;
+}
+
+.copy-wikitext-btn:disabled:hover {
+  background-color: #ccc;
+  transform: none;
+  box-shadow: none;
+}
+
+.wiki-insp-notice {
+  background-color: #fff3cd;
+  border: 1px solid #ffeaa7;
+  border-radius: 6px;
+  padding: 12px;
+  color: #856404;
+  font-size: 14px;
+}
+
+.wiki-insp-notice p {
+  margin: 0;
+}
+
+/* Wiki/Insp button styles */
+.wiki-insp-btn {
+  background-color: #9c27b0;
+  color: white;
+  padding: 8px 12px;
+  border-radius: 4px;
+  font-size: 14px;
+  font-weight: 500;
+}
+
+.wiki-insp-btn:hover {
+  background-color: #7b1fa2;
+}

+ 50 - 0
popup/popup.html

@@ -30,6 +30,11 @@
         <h1>Save Channel</h1>
       </header>
 
+      <header x-bind="wikiInspHeader">
+        <button class="back-btn-arrow" x-bind="backButton">←</button>
+        <h1>Wiki/Insp</h1>
+      </header>
+
       <div class="playlists-container" x-bind="playlistsContainer">
         <template
           x-for="playlistData in playlistsForDisplay"
@@ -245,6 +250,48 @@
         </div>
       </div>
 
+      <!-- Wiki/Insp view -->
+      <div class="wiki-insp-container" x-bind="wikiInspContainer">
+        <div class="wiki-insp-content">
+          <div class="wikitext-command-section">
+            <textarea
+              class="wikitext-command-field"
+              x-text="wikitextCommand"
+              readonly
+            ></textarea>
+            <div class="timestamp-controls">
+              <div class="timestamp-checkbox-container">
+                <label class="timestamp-checkbox-label">
+                  <input
+                    type="checkbox"
+                    class="timestamp-checkbox"
+                    x-bind="timestampCheckbox"
+                  />
+                  Include timestamp line
+                </label>
+              </div>
+              <button
+                class="refresh-timestamp-btn"
+                x-bind="refreshTimestampButton"
+                title="Refresh current video timestamp"
+              >
+                🔄 Refresh
+              </button>
+            </div>
+            <button
+              class="copy-wikitext-btn"
+              x-bind="copyWikitextButton"
+              title="Copy to clipboard"
+            >
+              Copy
+            </button>
+          </div>
+          <div class="wiki-insp-notice" x-bind="wikiInspNotice">
+            <p>This feature is only available on YouTube video pages (/watch).</p>
+          </div>
+        </div>
+      </div>
+
       <!-- Individual playlist view -->
       <div class="playlist-view-container" x-bind="playlistViewContainer">
         <div class="playlist-full-view">
@@ -346,6 +393,9 @@
         <button class="save-channel-btn" x-bind="saveChannelButton">
           Save Channel
         </button>
+        <button class="wiki-insp-btn" x-bind="wikiInspButton">
+          Wiki/Insp
+        </button>
         <input
           type="file"
           id="import-file-input"

+ 194 - 2
popup/popup.js

@@ -13,17 +13,18 @@ document.addEventListener("alpine:init", () => {
   //   - or raw json editing
   // X option (probably a button) to add current page (url, title) to playlist
   //   X align with addLinkToPlaylist in background.js (no repeated videos)
-  // - button to add channel to youtube page (copy gql mutation to clipboard) (like the automa version)
+  // X button to add channel to youtube page (copy gql mutation to clipboard) (like the automa version)
   // - long-term: replace youtube page? rss feeds? need server?
   // X add personal rating feature ("enjoyed", "this was important", etc)
   // - option to move video (including status) to another playlist
+  // X button to copy current video to clipboard in wikitext format (for personal wiki list)
   Alpine.data("playlistManager", () => ({
     playlists: {},
     currentIndices: {},
     history: {},
     sortedHistory: [],
     openMenus: new Set(), // Track which menus are open
-    currentView: "playlists", // Track current view: 'playlists', 'history', 'playlist', or 'saveChannel'
+    currentView: "playlists", // Track current view: 'playlists', 'history', 'playlist', 'saveChannel', or 'wikiInsp'
     currentPlaylistName: "", // Track which playlist is being viewed
     playlistsForDisplay: [], // Computed array for display
     currentPlaylistVideos: [], // Videos for current playlist view
@@ -52,6 +53,13 @@ document.addEventListener("alpine:init", () => {
     curlCommand: "", // Generated curl command
     checkInterval: 14, // Check interval in days
 
+    // Wiki/insp properties
+    wikitextCommand: "", // Generated wikitext
+    isCurrentVideoYoutube: false, // Whether current video is YouTube
+    hasVideoEnded: false, // Whether video has ended
+    currentVideoTimestamp: "", // Current video timestamp
+    includeTimestamp: true, // Whether to include timestamp line
+
     init() {
       this.loadPlaylists();
       this.loadHistory();
@@ -172,16 +180,79 @@ document.addEventListener("alpine:init", () => {
           this.isCurrentTabYoutube = url.hostname === "www.youtube.com";
           this.isCurrentTabChannelPage =
             this.isCurrentTabYoutube && url.pathname.includes("/videos");
+          this.isCurrentVideoYoutube =
+            this.isCurrentTabYoutube && url.pathname.includes("/watch");
+
+          // Get video timestamp if on YouTube video page
+          if (this.isCurrentVideoYoutube) {
+            await this.getVideoTimestamp();
+          }
+
           this.updateAddCurrentPageButtonText();
           this.updateCurlCommand();
+          this.updateWikitextCommand();
         }
       } catch (error) {
         console.error("Error getting current tab:", error);
         this.currentTab = null;
         this.isCurrentTabYoutube = false;
         this.isCurrentTabChannelPage = false;
+        this.isCurrentVideoYoutube = false;
         this.updateAddCurrentPageButtonText();
         this.updateCurlCommand();
+        this.updateWikitextCommand();
+      }
+    },
+
+    async getVideoTimestamp() {
+      try {
+        // Execute script in the current tab to get video timestamp
+        const results = await browser.tabs.executeScript(this.currentTab.id, {
+          code: `
+            (function() {
+              const video = document.querySelector('video');
+              if (video) {
+                const currentTime = video.currentTime;
+                const duration = video.duration;
+                const ended = video.ended;
+
+                const formatTime = (seconds) => {
+                  const hours = Math.floor(seconds / 3600);
+                  const minutes = Math.floor((seconds % 3600) / 60);
+                  const remainingSeconds = Math.floor(seconds % 60);
+
+                  if (hours > 0) {
+                    return hours + ':' + minutes.toString().padStart(2, '0') + ':' + remainingSeconds.toString().padStart(2, '0');
+                  } else {
+                    return minutes + ':' + remainingSeconds.toString().padStart(2, '0');
+                  }
+                };
+
+                return {
+                  currentTime: currentTime,
+                  formattedTime: formatTime(currentTime),
+                  duration: duration,
+                  ended: ended,
+                  isLongVideo: duration > 3600
+                };
+              }
+              return null;
+            })();
+          `
+        });
+
+        if (results && results[0]) {
+          const videoData = results[0];
+          this.currentVideoTimestamp = videoData.formattedTime;
+          this.hasVideoEnded = videoData.ended;
+        } else {
+          this.currentVideoTimestamp = "0:00";
+          this.hasVideoEnded = false;
+        }
+      } catch (error) {
+        console.error("Error getting video timestamp:", error);
+        this.currentVideoTimestamp = "0:00";
+        this.hasVideoEnded = false;
       }
     },
 
@@ -250,6 +321,75 @@ document.addEventListener("alpine:init", () => {
       this.updateCurlCommand();
     },
 
+    showWikiInsp() {
+      this.currentView = "wikiInsp";
+      // Refresh timestamp when opening the view
+      if (this.isCurrentVideoYoutube) {
+        this.getVideoTimestamp().then(() => {
+          this.updateWikitextCommand();
+        });
+      } else {
+        this.updateWikitextCommand();
+      }
+    },
+
+    async refreshVideoTimestamp() {
+      if (this.isCurrentVideoYoutube) {
+        await this.getVideoTimestamp();
+        this.updateWikitextCommand();
+      }
+    },
+
+    updateWikitextCommand() {
+      if (!this.currentTab || !this.isCurrentVideoYoutube) {
+        this.wikitextCommand = "This feature is only available on YouTube videos.";
+        return;
+      }
+
+      const today = new Date();
+      const dateString = today.toISOString().split('T')[0]; // YYYY-MM-DD format
+
+      const title = this.currentTab.title.replace(" - YouTube", "").replace(/\|/g, "-");
+      const url = this.currentTab.url;
+
+      let wikitext = `!!! ${dateString}\n* [[${title}|${url}]] (yt)`;
+
+      // Add timestamp if user has enabled it and video hasn't ended
+      if (this.includeTimestamp && !this.hasVideoEnded) {
+        wikitext += `\n** ${this.currentVideoTimestamp || "0:00"} - `;
+      }
+
+      this.wikitextCommand = wikitext;
+    },
+
+    async copyWikitextCommand() {
+      try {
+        await navigator.clipboard.writeText(this.wikitextCommand);
+        console.log("Wikitext copied to clipboard");
+        // Could add visual feedback here
+      } catch (error) {
+        console.error("Failed to copy to clipboard:", error);
+        // Fallback for older browsers
+        try {
+          const textArea = document.createElement("textarea");
+          textArea.value = this.wikitextCommand;
+          document.body.appendChild(textArea);
+          textArea.focus();
+          textArea.select();
+          document.execCommand("copy");
+          document.body.removeChild(textArea);
+          console.log("Wikitext copied to clipboard (fallback)");
+        } catch (fallbackError) {
+          console.error("Fallback copy failed:", fallbackError);
+        }
+      }
+    },
+
+    toggleTimestamp() {
+      this.includeTimestamp = !this.includeTimestamp;
+      this.updateWikitextCommand();
+    },
+
     sortHistoryByRecentInteraction() {
       // Convert history object to array with video ID and sort by most recent interaction
       this.sortedHistory = Object.entries(this.history)
@@ -919,6 +1059,12 @@ document.addEventListener("alpine:init", () => {
       },
     },
 
+    wikiInspButton: {
+      ["@click"]() {
+        this.showWikiInsp();
+      },
+    },
+
     backButton: {
       ["@click"]() {
         this.showPlaylists();
@@ -986,6 +1132,12 @@ document.addEventListener("alpine:init", () => {
       },
     },
 
+    wikiInspHeader: {
+      ["x-show"]() {
+        return this.currentView === "wikiInsp";
+      },
+    },
+
     playlistsContainer: {
       ["x-show"]() {
         return this.currentView === "playlists";
@@ -1010,6 +1162,12 @@ document.addEventListener("alpine:init", () => {
       },
     },
 
+    wikiInspContainer: {
+      ["x-show"]() {
+        return this.currentView === "wikiInsp";
+      },
+    },
+
     exportContainer: {
       ["x-show"]() {
         return this.currentView === "playlists";
@@ -1056,6 +1214,34 @@ document.addEventListener("alpine:init", () => {
       },
     },
 
+    // Wiki/insp specific bindings
+    copyWikitextButton: {
+      ["@click"]() {
+        this.copyWikitextCommand();
+      },
+      [":disabled"]() {
+        return !this.isCurrentVideoYoutube;
+      },
+    },
+
+    timestampCheckbox: {
+      ["@change"]() {
+        this.toggleTimestamp();
+      },
+      [":checked"]() {
+        return this.includeTimestamp;
+      },
+    },
+
+    refreshTimestampButton: {
+      ["@click"]() {
+        this.refreshVideoTimestamp();
+      },
+      [":disabled"]() {
+        return !this.isCurrentVideoYoutube;
+      },
+    },
+
     categoryButton: {
       ["@click"]() {
         const category = this.$el.dataset.category;
@@ -1075,6 +1261,12 @@ document.addEventListener("alpine:init", () => {
       },
     },
 
+    wikiInspNotice: {
+      ["x-show"]() {
+        return !this.isCurrentVideoYoutube;
+      },
+    },
+
     intervalInput: {
       [":value"]() {
         return this.checkInterval;