Skip to content

Commit dcf6479

Browse files
Decrease mobile mock window height in editor section (#1986)
* Decrease mobile mock window height in editor section Co-Authored-By: [email protected] <[email protected]> * Mimic Notion-style typing animation for markdown Introduce staged placeholder and transformation states to make the typing animation mimic how tools like Notion render headings and lists. Added showPlaceholder and transforming state flags, expanded lines to include placeholder text, and adjusted the typing logic to briefly display a placeholder (e.g., "Heading 1" or "List") after typing the trigger sequence ("# " or "- ") before animating into the transformed rendered element. Also updated rendering to show a pulsing cursor during placeholder and to animate transformed headings with motion. * Keep cursor placeholder in-place during typing Prevent the cursor from jumping to the end by rendering the typing cursor inline with the typing text and placeholder. The change moves the pulse cursor character into the same span as typingText and places the placeholder in a following inline span so the cursor stays at the current typing position instead of jumping to the end. * Reduce space between cursor and placeholder Adjust the spacing between the blinking cursor and the placeholder text to make the caret appear closer to the placeholder. The change replaces the margin-left utility from ml-2 to ml-0.5 to tighten the gap and improve visual alignment of the cursor and placeholder in the AI note-taking component. * Show placeholder and blinking cursor for headings and bullets Make the placeholder text and blinking cursor visible immediately after typing a marker (e.g. "# " or "- ") and render them in the correct visual style for headings and bullets. This change replaces generic placeholders like "Heading 1"/"List" with "Enter header"/"Enter list item", ensures the cursor blinks before the placeholder, and uses larger heading font sizes and proper list markup so the placeholder matches the final rendered element. * Avoid re-animating list item placeholder Prevent transformed list items and other placeholder-rendered lines from re-sliding by removing motion wrappers for heading (and adding comments for bullet and bold states). This stops the slide-in animation when the placeholder is shown so the UI doesn't animate the item again after transformation; the diff removes the motion.h1 wrapper and adds clarifying comments for bullet and bold transformed states. * Don't animate when placeholders disappear Remove placeholder-related animation state and transitional opacity so placeholders vanish instantly instead of animating. The change eliminates the "transforming" state and related class toggles, and ensures showPlaceholder is reset immediately when placeholders are dismissed. This prevents unwanted fade/opacity animations when placeholders go away and simplifies the placeholder/typing flow. * feat(product): add default typing state comment --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent 330d811 commit dcf6479

File tree

1 file changed

+94
-12
lines changed

1 file changed

+94
-12
lines changed

apps/web/src/routes/_view/product/ai-notetaking.tsx

Lines changed: 94 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ function EditorSection() {
281281
</div>
282282
<div className="px-6 pb-0 bg-stone-50/30 overflow-clip">
283283
<MockWindow variant="mobile">
284-
<div className="p-6 h-[380px] overflow-hidden">
284+
<div className="p-6 h-[200px] overflow-hidden">
285285
<AnimatedMarkdownDemo isMobile />
286286
</div>
287287
</MockWindow>
@@ -380,15 +380,33 @@ function AnimatedMarkdownDemo({ isMobile = false }: { isMobile?: boolean }) {
380380
const [currentLineIndex, setCurrentLineIndex] = useState(0);
381381
const [typingText, setTypingText] = useState("");
382382
const [isTransformed, setIsTransformed] = useState(false);
383+
const [showPlaceholder, setShowPlaceholder] = useState(false);
383384

384385
const lines = [
385-
{ text: "# Meeting Notes", type: "heading" as const },
386-
{ text: "- Product roadmap review", type: "bullet" as const },
387-
{ text: "- Q4 marketing strategy", type: "bullet" as const },
388-
{ text: "- Budget allocation", type: "bullet" as const },
386+
{
387+
text: "# Meeting Notes",
388+
type: "heading" as const,
389+
placeholder: "Enter header",
390+
},
391+
{
392+
text: "- Product roadmap review",
393+
type: "bullet" as const,
394+
placeholder: "Enter list item",
395+
},
396+
{
397+
text: "- Q4 marketing strategy",
398+
type: "bullet" as const,
399+
placeholder: "Enter list item",
400+
},
401+
{
402+
text: "- Budget allocation",
403+
type: "bullet" as const,
404+
placeholder: "Enter list item",
405+
},
389406
{
390407
text: "**Decision:** Launch campaign by end of month",
391408
type: "bold" as const,
409+
placeholder: "",
392410
},
393411
];
394412

@@ -399,6 +417,7 @@ function AnimatedMarkdownDemo({ isMobile = false }: { isMobile?: boolean }) {
399417
setCurrentLineIndex(0);
400418
setTypingText("");
401419
setIsTransformed(false);
420+
setShowPlaceholder(false);
402421
}, 2000);
403422
return () => clearTimeout(timeout);
404423
}
@@ -413,16 +432,29 @@ function AnimatedMarkdownDemo({ isMobile = false }: { isMobile?: boolean }) {
413432
setTypingText(newText);
414433
charIndex++;
415434

416-
const shouldTransform =
435+
// Check if we just typed the trigger sequence (e.g., "# " or "- ")
436+
const isMarkdownTrigger =
417437
(currentLine.type === "heading" && newText === "# ") ||
418-
(currentLine.type === "bullet" && newText === "- ") ||
419-
(currentLine.type === "bold" && newText.match(/\*\*[^*]+\*\*/));
420-
421-
if (shouldTransform) {
438+
(currentLine.type === "bullet" && newText === "- ");
439+
440+
if (isMarkdownTrigger && !isTransformed) {
441+
// Show placeholder briefly, then transform
442+
setShowPlaceholder(true);
443+
444+
timeout = setTimeout(() => {
445+
setIsTransformed(true);
446+
setShowPlaceholder(false);
447+
timeout = setTimeout(typeCharacter, 60);
448+
}, 300); // Show placeholder duration
449+
} else if (
450+
currentLine.type === "bold" &&
451+
newText.match(/\*\*[^*]+\*\*/)
452+
) {
422453
setIsTransformed(true);
454+
timeout = setTimeout(typeCharacter, 60);
455+
} else {
456+
timeout = setTimeout(typeCharacter, 60);
423457
}
424-
425-
timeout = setTimeout(typeCharacter, 60);
426458
} else {
427459
timeout = setTimeout(() => {
428460
const completedElement = renderCompletedLine(currentLine, isMobile);
@@ -432,6 +464,7 @@ function AnimatedMarkdownDemo({ isMobile = false }: { isMobile?: boolean }) {
432464

433465
setTypingText("");
434466
setIsTransformed(false);
467+
setShowPlaceholder(false);
435468
setCurrentLineIndex((prev) => prev + 1);
436469
}, 800);
437470
}
@@ -509,6 +542,52 @@ function AnimatedMarkdownDemo({ isMobile = false }: { isMobile?: boolean }) {
509542
return null;
510543
}
511544

545+
// Show placeholder state (after typing "# " or "- " but before transformation)
546+
if (showPlaceholder && !isTransformed) {
547+
// For headings, show with larger font size
548+
if (currentLine.type === "heading") {
549+
return (
550+
<h1 className={cn(["font-bold", isMobile ? "text-xl" : "text-2xl"])}>
551+
<span className="animate-pulse">|</span>
552+
<span className="text-neutral-400">{currentLine.placeholder}</span>
553+
</h1>
554+
);
555+
}
556+
557+
// For bullets, show as list item
558+
if (currentLine.type === "bullet") {
559+
return (
560+
<ul
561+
className={cn([
562+
"list-disc pl-5",
563+
isMobile ? "text-sm" : "text-base",
564+
])}
565+
>
566+
<li>
567+
<span className="animate-pulse">|</span>
568+
<span className="text-neutral-400">
569+
{currentLine.placeholder}
570+
</span>
571+
</li>
572+
</ul>
573+
);
574+
}
575+
576+
// Default fallback (shouldn't reach here for current lines)
577+
return (
578+
<div
579+
className={cn([
580+
"text-neutral-700",
581+
isMobile ? "text-sm" : "text-base",
582+
])}
583+
>
584+
<span className="animate-pulse">|</span>
585+
<span className="text-neutral-400">{currentLine.placeholder}</span>
586+
</div>
587+
);
588+
}
589+
590+
// Transformed state for headings
512591
if (currentLine.type === "heading" && isTransformed) {
513592
const displayText = typingText.slice(2); // Remove "# "
514593
return (
@@ -524,6 +603,7 @@ function AnimatedMarkdownDemo({ isMobile = false }: { isMobile?: boolean }) {
524603
);
525604
}
526605

606+
// Transformed state for bullets
527607
if (currentLine.type === "bullet" && isTransformed) {
528608
const displayText = typingText.slice(2); // Remove "- "
529609
return (
@@ -541,6 +621,7 @@ function AnimatedMarkdownDemo({ isMobile = false }: { isMobile?: boolean }) {
541621
);
542622
}
543623

624+
// Transformed state for bold text
544625
if (currentLine.type === "bold" && isTransformed) {
545626
const parts = typingText.split(/(\*\*.*?\*\*)/g);
546627
return (
@@ -565,6 +646,7 @@ function AnimatedMarkdownDemo({ isMobile = false }: { isMobile?: boolean }) {
565646
);
566647
}
567648

649+
// Default typing state
568650
return (
569651
<div
570652
className={cn(["text-neutral-700", isMobile ? "text-sm" : "text-base"])}

0 commit comments

Comments
 (0)