diff --git a/packages/editor/App.tsx b/packages/editor/App.tsx index 405c07e..90c107c 100644 --- a/packages/editor/App.tsx +++ b/packages/editor/App.tsx @@ -36,6 +36,8 @@ import { PermissionModeSetup } from '@plannotator/ui/components/PermissionModeSe import { UIFeaturesSetup } from '@plannotator/ui/components/UIFeaturesSetup'; import { PlanDiffMarketing } from '@plannotator/ui/components/plan-diff/PlanDiffMarketing'; import { needsPlanDiffMarketingDialog } from '@plannotator/ui/utils/planDiffMarketing'; +import { WhatsNewV011 } from '@plannotator/ui/components/WhatsNewV011'; +import { needsWhatsNewDialog } from '@plannotator/ui/utils/whatsNew'; import { ImageAnnotator } from '@plannotator/ui/components/ImageAnnotator'; import { deriveImageName } from '@plannotator/ui/components/AttachmentsButton'; import { useSidebar } from '@plannotator/ui/hooks/useSidebar'; @@ -372,6 +374,7 @@ const App: React.FC = () => { const [showPermissionModeSetup, setShowPermissionModeSetup] = useState(false); const [showUIFeaturesSetup, setShowUIFeaturesSetup] = useState(false); const [showPlanDiffMarketing, setShowPlanDiffMarketing] = useState(false); + const [showWhatsNew, setShowWhatsNew] = useState(false); const [permissionMode, setPermissionMode] = useState('bypassPermissions'); const [sharingEnabled, setSharingEnabled] = useState(true); const [shareBaseUrl, setShareBaseUrl] = useState(undefined); @@ -619,6 +622,8 @@ const App: React.FC = () => { setShowUIFeaturesSetup(true); } else if (needsPlanDiffMarketingDialog()) { setShowPlanDiffMarketing(true); + } else if (needsWhatsNewDialog()) { + setShowWhatsNew(true); } // Load saved permission mode preference setPermissionMode(getPermissionModeSettings().mode); @@ -806,7 +811,7 @@ const App: React.FC = () => { // Don't intercept if any modal is open if (showExport || showImport || showFeedbackPrompt || showClaudeCodeWarning || - showAgentWarning || showPermissionModeSetup || showUIFeaturesSetup || showPlanDiffMarketing || pendingPasteImage) return; + showAgentWarning || showPermissionModeSetup || showUIFeaturesSetup || showPlanDiffMarketing || showWhatsNew || pendingPasteImage) return; // Don't intercept if already submitted or submitting if (submitted || isSubmitting) return; @@ -850,7 +855,7 @@ const App: React.FC = () => { return () => window.removeEventListener('keydown', handleKeyDown); }, [ showExport, showImport, showFeedbackPrompt, showClaudeCodeWarning, showAgentWarning, - showPermissionModeSetup, showUIFeaturesSetup, showPlanDiffMarketing, pendingPasteImage, + showPermissionModeSetup, showUIFeaturesSetup, showPlanDiffMarketing, showWhatsNew, pendingPasteImage, submitted, isSubmitting, isApiMode, linkedDocHook.isActive, annotations.length, annotateMode, origin, getAgentWarning, ]); @@ -977,7 +982,7 @@ const App: React.FC = () => { if (tag === 'INPUT' || tag === 'TEXTAREA') return; if (showExport || showFeedbackPrompt || showClaudeCodeWarning || - showAgentWarning || showPermissionModeSetup || showUIFeaturesSetup || showPlanDiffMarketing || pendingPasteImage) return; + showAgentWarning || showPermissionModeSetup || showUIFeaturesSetup || showPlanDiffMarketing || showWhatsNew || pendingPasteImage) return; if (submitted || !isApiMode) return; @@ -1003,7 +1008,7 @@ const App: React.FC = () => { return () => window.removeEventListener('keydown', handleSaveShortcut); }, [ showExport, showFeedbackPrompt, showClaudeCodeWarning, showAgentWarning, - showPermissionModeSetup, showUIFeaturesSetup, showPlanDiffMarketing, pendingPasteImage, + showPermissionModeSetup, showUIFeaturesSetup, showPlanDiffMarketing, showWhatsNew, pendingPasteImage, submitted, isApiMode, markdown, annotationsOutput, ]); @@ -1539,6 +1544,8 @@ const App: React.FC = () => { setShowUIFeaturesSetup(true); } else if (needsPlanDiffMarketingDialog()) { setShowPlanDiffMarketing(true); + } else if (needsWhatsNewDialog()) { + setShowWhatsNew(true); } }} /> @@ -1551,6 +1558,8 @@ const App: React.FC = () => { setShowUIFeaturesSetup(false); if (needsPlanDiffMarketingDialog()) { setShowPlanDiffMarketing(true); + } else if (needsWhatsNewDialog()) { + setShowWhatsNew(true); } }} /> @@ -1561,6 +1570,17 @@ const App: React.FC = () => { origin={origin} onComplete={() => { setShowPlanDiffMarketing(false); + if (needsWhatsNewDialog()) { + setShowWhatsNew(true); + } + }} + /> + + {/* What's New v0.11.0 (feature announcement) */} + { + setShowWhatsNew(false); }} /> diff --git a/packages/ui/components/WhatsNewV011.tsx b/packages/ui/components/WhatsNewV011.tsx new file mode 100644 index 0000000..d91f83f --- /dev/null +++ b/packages/ui/components/WhatsNewV011.tsx @@ -0,0 +1,113 @@ +import React from 'react'; +import { createPortal } from 'react-dom'; +import { markWhatsNewSeen } from '../utils/whatsNew'; + +const RELEASE_URLS = { + v0100: 'https://github.com/backnotprop/plannotator/releases/tag/v0.10.0', + v0110: 'https://github.com/backnotprop/plannotator/releases/tag/v0.11.0', +}; + +interface WhatsNewV011Props { + isOpen: boolean; + onComplete: () => void; +} + +export const WhatsNewV011: React.FC = ({ + isOpen, + onComplete, +}) => { + if (!isOpen) return null; + + const handleDismiss = () => { + markWhatsNewSeen(); + onComplete(); + }; + + return createPortal( +
+
+ {/* Header */} +
+
+
+ + + +
+

What's New in Plannotator

+
+

+ Here's what's been added since your last update. +

+
+ + {/* Content */} +
+ {/* Feature bullets */} +
+
+
+
+
+

+ Auto-save annotation drafts{' '} + — Your annotations are now automatically saved in the background. If the browser refreshes or the server crashes, you'll be prompted to restore your work. Sorry it took so long to implement this. +

+
+
+
+
+
+

+ Short link sharing{' '} + — Share plans with shorter, more portable links that work across Slack and other platforms, with end-to-end encryption. Currently enabled for large plans that don't fit in the URL; we'll be rolling this out as the default soon. +

+
+
+
+
+
+

+ Obsidian vault browser{' '} + — For Obsidian users, we're building deeper integrations starting with the vault browser. Browse and annotate vault files directly during plan review. Toggle it on under Settings > Saving > Obsidian. +

+
+
+ + {/* Release notes callout */} +

+ Plus many more improvements and bug fixes. See the full release notes:{' '} + + v0.10.0 + + {', '} + + v0.11.0 + +

+
+ + {/* Footer */} +
+ +
+
+
, + document.body + ); +}; diff --git a/packages/ui/utils/whatsNew.ts b/packages/ui/utils/whatsNew.ts new file mode 100644 index 0000000..b124778 --- /dev/null +++ b/packages/ui/utils/whatsNew.ts @@ -0,0 +1,11 @@ +import { storage } from './storage'; + +const STORAGE_KEY = 'plannotator-whats-new-v011-seen'; + +export function needsWhatsNewDialog(): boolean { + return storage.getItem(STORAGE_KEY) !== 'true'; +} + +export function markWhatsNewSeen(): void { + storage.setItem(STORAGE_KEY, 'true'); +}