diff --git a/hindsight-docs/blog/2026-02-09-resolving-memory-conflicts.md b/hindsight-docs/blog/2026-02-09-resolving-memory-conflicts.md index 30ef48c9..9a1023c8 100644 --- a/hindsight-docs/blog/2026-02-09-resolving-memory-conflicts.md +++ b/hindsight-docs/blog/2026-02-09-resolving-memory-conflicts.md @@ -4,6 +4,7 @@ description: Learn how Hindsight handles contradictory information by tracking t authors: [hindsight] image: /img/blog/2026-02-09/consolidation-pipeline.png date: 2026-02-09 +hide_table_of_contents: true --- # How We Solved Memory Conflicts in Hindsight diff --git a/hindsight-docs/blog/2026-02-13-version-0-4-11.md b/hindsight-docs/blog/2026-02-13-version-0-4-11.md index daf10b08..9c07ba25 100644 --- a/hindsight-docs/blog/2026-02-13-version-0-4-11.md +++ b/hindsight-docs/blog/2026-02-13-version-0-4-11.md @@ -3,6 +3,7 @@ title: "What's new in Hindsight 0.4.11" description: New features and improvements in Hindsight 0.4.11 authors: [hindsight] date: 2026-02-13 +hide_table_of_contents: true --- Hindsight 0.4.11 focuses on production-ready deployments with improved flexibility and observability. diff --git a/hindsight-docs/blog/2026-02-18-version-0-4-12.md b/hindsight-docs/blog/2026-02-18-version-0-4-12.md index 6b99181d..dd6128a9 100644 --- a/hindsight-docs/blog/2026-02-18-version-0-4-12.md +++ b/hindsight-docs/blog/2026-02-18-version-0-4-12.md @@ -3,6 +3,7 @@ title: "What's new in Hindsight 0.4.12" description: New features and improvements in Hindsight 0.4.12 authors: [hindsight] date: 2026-02-18 +hide_table_of_contents: true --- Hindsight 0.4.12 expands what you can ingest, cuts ingestion costs, and broadens where you can run it. diff --git a/hindsight-docs/blog/2026-02-26-chat.md b/hindsight-docs/blog/2026-02-26-chat.md index fa38a6d8..598cf618 100644 --- a/hindsight-docs/blog/2026-02-26-chat.md +++ b/hindsight-docs/blog/2026-02-26-chat.md @@ -1,11 +1,13 @@ --- -title: "I Gave My Vercel Chat SDK Bot a Memory. Now It Remembers Users Across Slack and Discord." +title: "Your Vercel Chat SDK bot forgets everything. Hindsight fixes that." authors: [hindsight] date: 2026-02-26 tags: [chat-sdk, slack, discord, typescript, memory] +image: /img/blog/vercel-chat.png --- -# I Gave My Vercel Chat SDK Bot a Memory. Now It Remembers Users Across Slack and Discord. + +![Consolidation Pipeline](/img/blog/vercel-chat.png) ## TL;DR diff --git a/hindsight-docs/docs/sdks/integrations/chat.md b/hindsight-docs/docs/sdks/integrations/chat.md index 8b024f52..a35b3b18 100644 --- a/hindsight-docs/docs/sdks/integrations/chat.md +++ b/hindsight-docs/docs/sdks/integrations/chat.md @@ -4,7 +4,7 @@ sidebar_position: 5 # Vercel Chat SDK -The `@vectorize-io/hindsight-chat` package gives your [Vercel Chat SDK](https://github.com/vercel/chat) bots persistent, per-user memory with a single handler wrapper. Works with Slack, Discord, Teams, Google Chat, GitHub, and Linear. +We built `@vectorize-io/hindsight-chat` to give [Vercel Chat SDK](https://github.com/vercel/chat) bots persistent, per-user memory with a single handler wrapper. The integration works across Slack, Discord, Teams, Google Chat, GitHub, and Linear β€” no custom plumbing required. ## Installation @@ -60,7 +60,7 @@ chat.onNewMention( ### `withHindsightChat(options, handler)` -Returns a standard Chat SDK handler `(thread, message) => Promise`. +`withHindsightChat` wraps your existing Chat SDK handler and injects memory context automatically. It returns a standard handler `(thread, message) => Promise` so it drops in without changing your handler signature. #### Options @@ -80,7 +80,7 @@ Returns a standard Chat SDK handler `(thread, message) => Promise`. ### Context (`ctx`) -The third argument passed to your handler: +We inject a third `ctx` argument into your handler that exposes the full Hindsight memory API scoped to the current user's bank: | Property/Method | Description | |----------------|-------------| @@ -160,4 +160,4 @@ chat.onNewMention( ## Error Handling -Memory failures never break your bot. Auto-recall and auto-retain errors are logged as warnings and the handler continues with empty memories. Manual `ctx.retain()`, `ctx.recall()`, and `ctx.reflect()` calls propagate errors normally so you can handle them as needed. +We designed the integration so that memory failures never break your bot. Auto-recall and auto-retain errors are caught internally, logged as warnings, and the handler continues with empty memories. Manual `ctx.retain()`, `ctx.recall()`, and `ctx.reflect()` calls propagate errors normally so you can handle them as needed. diff --git a/hindsight-docs/docusaurus.config.ts b/hindsight-docs/docusaurus.config.ts index 3185b103..9daef52e 100644 --- a/hindsight-docs/docusaurus.config.ts +++ b/hindsight-docs/docusaurus.config.ts @@ -117,8 +117,7 @@ const config: Config = { blogTitle: 'Hindsight Blog', blogDescription: 'Updates, insights, and deep dives into agent memory', postsPerPage: 10, - blogSidebarTitle: 'Recent posts', - blogSidebarCount: 'ALL', + blogSidebarCount: 0, }, theme: { customCss: './src/css/custom.css', @@ -235,8 +234,7 @@ const config: Config = { className: 'navbar-item-resources', items: [ { - type: 'doc', - docId: 'cookbook/index', + to: '/cookbook', label: 'Cookbook', }, { @@ -247,22 +245,22 @@ const config: Config = { to: '/api-reference', label: 'API Reference', }, + { + href: 'https://join.slack.com/t/hindsight-space/shared_invite/zt-3nhbm4w29-LeSJ5Ixi6j8PdiYOCPlOgg', + label: 'Community', + }, ], }, { href: 'https://ui.hindsight.vectorize.io/signup', position: 'right', - label: 'Hindsight Cloud', + label: 'Cloud', className: 'navbar-item-cloud', }, { type: 'docsVersionDropdown', position: 'right', - }, - { - href: 'https://join.slack.com/t/hindsight-space/shared_invite/zt-3nhbm4w29-LeSJ5Ixi6j8PdiYOCPlOgg', - position: 'right', - label: 'Community', + className: 'navbar-item-version', }, { href: 'https://github.com/vectorize-io/hindsight', diff --git a/hindsight-docs/sidebars.ts b/hindsight-docs/sidebars.ts index b0b16fba..817b26bf 100644 --- a/hindsight-docs/sidebars.ts +++ b/hindsight-docs/sidebars.ts @@ -234,13 +234,6 @@ const sidebars: SidebarsConfig = { ], }, ], - cookbookSidebar: [ - { - type: 'doc', - id: 'cookbook/index', - label: 'Cookbook', - }, - ], }; export default sidebars; diff --git a/hindsight-docs/src/components/CookbookGrid.module.css b/hindsight-docs/src/components/CookbookGrid.module.css new file mode 100644 index 00000000..7a09eac1 --- /dev/null +++ b/hindsight-docs/src/components/CookbookGrid.module.css @@ -0,0 +1,115 @@ +.grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.25rem; + margin-bottom: 3rem; +} + +@media (max-width: 996px) { + .grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 640px) { + .grid { + grid-template-columns: 1fr; + } +} + +/* Card */ +.card { + display: flex; + flex-direction: column; + border-radius: 10px; + border: 1px solid var(--ifm-color-emphasis-200); + border-top: 3px solid transparent; + border-image: linear-gradient(90deg, #0074d9, #009296) 1; + background: var(--ifm-background-surface-color); + text-decoration: none !important; + color: inherit; + transition: box-shadow 0.2s ease, transform 0.2s ease; + overflow: hidden; +} + +[data-theme='dark'] .card { + background: #1c1c1e; + border-color: rgba(255, 255, 255, 0.07); +} + +.card:hover { + transform: translateY(-2px); + box-shadow: 0 6px 24px rgba(0, 116, 217, 0.12); + text-decoration: none !important; + color: inherit; +} + +/* Body */ +.cardBody { + display: flex; + flex-direction: column; + flex: 1; + padding: 1.25rem 1.25rem 1.25rem; + gap: 0.4rem; +} + +.cardTitle { + font-size: 1rem; + font-weight: 700; + line-height: 1.4; + margin: 0; + color: var(--ifm-heading-color); + letter-spacing: -0.01em; + border-bottom: none !important; + border-image: none !important; +} + +.cardDescription { + font-size: 0.82rem; + font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', Monaco, Consolas, monospace; + color: var(--ifm-color-emphasis-700); + line-height: 1.6; + margin: 0; + flex: 1; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.cardFooter { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.4rem; + margin-top: 0.75rem; + padding-top: 0.65rem; + border-top: 1px solid var(--ifm-color-emphasis-100); +} + +[data-theme='dark'] .cardFooter { + border-top-color: rgba(255, 255, 255, 0.06); +} + +.cardTopic { + font-size: 0.72rem; + font-weight: 600; + color: var(--ifm-color-primary); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.cardSdk { + font-size: 0.72rem; + font-weight: 500; + font-family: 'JetBrains Mono', 'Fira Code', monospace; + color: var(--ifm-color-emphasis-700); + background: var(--ifm-color-emphasis-100); + padding: 0.1rem 0.45rem; + border-radius: 4px; +} + +[data-theme='dark'] .cardSdk { + background: rgba(255, 255, 255, 0.07); + color: var(--ifm-color-emphasis-600); +} diff --git a/hindsight-docs/src/components/CookbookGrid.tsx b/hindsight-docs/src/components/CookbookGrid.tsx new file mode 100644 index 00000000..81a7a6f7 --- /dev/null +++ b/hindsight-docs/src/components/CookbookGrid.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import Link from '@docusaurus/Link'; +import styles from './CookbookGrid.module.css'; + +export interface CookbookCard { + title: string; + href: string; + description?: string; + tags?: { + sdk?: string; + topic?: string; + }; +} + +interface CookbookGridProps { + items: CookbookCard[]; +} + +function Card({title, href, description, tags}: CookbookCard) { + return ( + +
+

{title}

+ {description &&

{description}

} + {(tags?.topic || tags?.sdk) && ( +
+ {tags.topic && {tags.topic}} + {tags.sdk && {tags.sdk}} +
+ )} +
+ + ); +} + +export default function CookbookGrid({items}: CookbookGridProps) { + return ( +
+ {items.map((item) => ( + + ))} +
+ ); +} diff --git a/hindsight-docs/src/components/RecipeCarousel.module.css b/hindsight-docs/src/components/RecipeCarousel.module.css deleted file mode 100644 index 32b5b9d4..00000000 --- a/hindsight-docs/src/components/RecipeCarousel.module.css +++ /dev/null @@ -1,181 +0,0 @@ -.carouselSection { - margin: 3rem 0; -} - -.sectionTitle { - font-size: 1.75rem; - margin-bottom: 1.5rem; - font-weight: 600; - color: var(--ifm-font-color-base); -} - -.carousel { - /* Grid layout */ -} - -.carouselTrack { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); - gap: 1.25rem; -} - -.card { - padding: 1.5rem; - border-radius: 12px; - border: 2px solid var(--card-border, var(--ifm-color-emphasis-300)); - text-decoration: none; - color: inherit; - display: flex; - flex-direction: column; - justify-content: space-between; - gap: 1rem; - transition: all 0.2s ease; - position: relative; - overflow: hidden; - min-height: 200px; -} - -/* Alternating style: Odd cards = white/solid, Even cards = colored gradient */ - -/* ODD CARDS - White/Solid background */ -.card:nth-child(odd) { - background: #ffffff; -} - -.card:nth-child(odd):hover { - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1); - transform: translateY(-2px); -} - -/* EVEN CARDS - Colored gradients (cycle through 4 colors) */ -.card:nth-child(4n+2) { - background: linear-gradient(135deg, rgba(0, 116, 217, 0.08) 0%, rgba(0, 146, 150, 0.08) 100%); -} - -.card:nth-child(4n+4) { - background: linear-gradient(135deg, rgba(99, 102, 241, 0.08) 0%, rgba(168, 85, 247, 0.08) 100%); -} - -.card:nth-child(4n+6) { - background: linear-gradient(135deg, rgba(16, 185, 129, 0.08) 0%, rgba(5, 150, 105, 0.08) 100%); -} - -.card:nth-child(4n+8) { - background: linear-gradient(135deg, rgba(245, 158, 11, 0.08) 0%, rgba(217, 119, 6, 0.08) 100%); -} - -.card:nth-child(even):hover { - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); - transform: translateY(-2px); -} - -/* ============================================ - DARK MODE - ============================================ */ - -/* ODD CARDS - Dark solid background */ -[data-theme='dark'] .card:nth-child(odd) { - background: var(--ifm-background-surface-color); -} - -[data-theme='dark'] .card { - border-color: var(--card-border-dark, var(--ifm-color-emphasis-300)); -} - -[data-theme='dark'] .card:nth-child(odd):hover { - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6); -} - -/* EVEN CARDS - Colored gradients (more vibrant in dark mode) */ -[data-theme='dark'] .card:nth-child(4n+2) { - background: linear-gradient(135deg, rgba(59, 130, 246, 0.15) 0%, rgba(20, 184, 166, 0.15) 100%); -} - -[data-theme='dark'] .card:nth-child(4n+4) { - background: linear-gradient(135deg, rgba(139, 92, 246, 0.15) 0%, rgba(217, 70, 239, 0.15) 100%); -} - -[data-theme='dark'] .card:nth-child(4n+6) { - background: linear-gradient(135deg, rgba(16, 185, 129, 0.15) 0%, rgba(132, 204, 22, 0.15) 100%); -} - -[data-theme='dark'] .card:nth-child(4n+8) { - background: linear-gradient(135deg, rgba(251, 146, 60, 0.15) 0%, rgba(239, 68, 68, 0.15) 100%); -} - -[data-theme='dark'] .card:nth-child(even):hover { - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.8); -} - -.cardContent { - display: flex; - flex-direction: column; - gap: 0.65rem; - flex-grow: 1; -} - -.cardFooter { - display: flex; - align-items: center; - justify-content: space-between; - margin-top: auto; -} - -.cardTitle { - font-size: 1.05rem; - font-weight: 600; - color: var(--ifm-font-color-base); - line-height: 1.4; -} - -.cardDescription { - font-size: 0.9rem; - color: var(--ifm-color-emphasis-800); - margin: 0; - line-height: 1.6; -} - -[data-theme='dark'] .cardDescription { - color: var(--ifm-color-emphasis-600); -} - -.cardTags { - display: flex; - flex-wrap: nowrap; - gap: 0.75rem; - align-items: center; - flex: 1; -} - -.tag { - font-size: 0.72rem; - padding: 0.35rem 0.75rem; - border-radius: 6px; - background: var(--tag-bg); - color: var(--tag-text); - font-weight: 600; - border: 1px solid var(--tag-border); - white-space: nowrap; - flex-shrink: 0; -} - -[data-theme='dark'] .tag { - background: var(--tag-bg-dark); - color: var(--tag-text-dark); - border-color: var(--tag-border-dark); -} - -.cardLink { - color: var(--ifm-color-primary); - font-weight: 600; - flex-shrink: 0; - font-size: 1.25rem; - line-height: 1; - opacity: 0.7; - transition: opacity 0.2s ease; - margin-left: 1rem; -} - -.card:hover .cardLink { - opacity: 1; -} diff --git a/hindsight-docs/src/components/RecipeCarousel.tsx b/hindsight-docs/src/components/RecipeCarousel.tsx deleted file mode 100644 index 4044cd7c..00000000 --- a/hindsight-docs/src/components/RecipeCarousel.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import React from 'react'; -import Link from '@docusaurus/Link'; -import styles from './RecipeCarousel.module.css'; - -export interface RecipeCard { - title: string; - href: string; - tags?: { - sdk?: string; // Package name: "hindsight-python", "hindsight-nodejs", "litellm-python", "ai-sdk", etc. - topic?: string; // "Learning", "Quick Start", "Recommendation", "Chat" - }; - description?: string; -} - -interface RecipeCarouselProps { - title: string; - items: RecipeCard[]; -} - -// Language icons using inline SVG data URIs or image paths -const LANGUAGE_ICONS: Record = { - Python: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%233776ab' d='M14.25.18l.9.2.73.26.59.3.45.32.34.34.25.34.16.33.1.3.04.26.02.2-.01.13V8.5l-.05.63-.13.55-.21.46-.26.38-.3.31-.33.25-.35.19-.35.14-.33.1-.3.07-.26.04-.21.02H8.77l-.69.05-.59.14-.5.22-.41.27-.33.32-.27.35-.2.36-.15.37-.1.35-.07.32-.04.27-.02.21v3.06H3.17l-.21-.03-.28-.07-.32-.12-.35-.18-.36-.26-.36-.36-.35-.46-.32-.59-.28-.73-.21-.88-.14-1.05-.05-1.23.06-1.22.16-1.04.24-.87.32-.71.36-.57.4-.44.42-.33.42-.24.4-.16.36-.1.32-.05.24-.01h.16l.06.01h8.16v-.83H6.18l-.01-2.75-.02-.37.05-.34.11-.31.17-.28.25-.26.31-.23.38-.2.44-.18.51-.15.58-.12.64-.1.71-.06.77-.04.84-.02 1.27.05zm-6.3 1.98l-.23.33-.08.41.08.41.23.34.33.22.41.09.41-.09.33-.22.23-.34.08-.41-.08-.41-.23-.33-.33-.22-.41-.09-.41.09zm13.09 3.95l.28.06.32.12.35.18.36.27.36.35.35.47.32.59.28.73.21.88.14 1.04.05 1.23-.06 1.23-.16 1.04-.24.86-.32.71-.36.57-.4.45-.42.33-.42.24-.4.16-.36.09-.32.05-.24.02-.16-.01h-8.22v.82h5.84l.01 2.76.02.36-.05.34-.11.31-.17.29-.25.25-.31.24-.38.2-.44.17-.51.15-.58.13-.64.09-.71.07-.77.04-.84.01-1.27-.04-1.07-.14-.9-.2-.73-.25-.59-.3-.45-.33-.34-.34-.25-.34-.16-.33-.1-.3-.04-.25-.02-.2.01-.13v-5.34l.05-.64.13-.54.21-.46.26-.38.3-.32.33-.24.35-.2.35-.14.33-.1.3-.06.26-.04.21-.02.13-.01h5.84l.69-.05.59-.14.5-.21.41-.28.33-.32.27-.35.2-.36.15-.36.1-.35.07-.32.04-.28.02-.21V6.07h2.09l.14.01zm-6.47 14.25l-.23.33-.08.41.08.41.23.33.33.23.41.08.41-.08.33-.23.23-.33.08-.41-.08-.41-.23-.33-.33-.23-.41-.08-.41.08z'/%3E%3C/svg%3E", - 'Node.js': "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23339933' d='M11.998 0c-.27 0-.54.07-.772.202L2.428 5.05C1.983 5.321 1.7 5.802 1.7 6.32v11.36c0 .518.283 1 .728 1.27l2.375 1.371c.64.321 1.094.32 1.468.32 1.203 0 1.89-.73 1.89-1.996V7.362c0-.146-.117-.264-.262-.264H7.11c-.146 0-.263.118-.263.264v11.283c0 .876-.906 1.753-2.38 1.01L2.103 18.28c-.046-.026-.073-.08-.073-.132V6.754c0-.051.027-.106.073-.132l8.798-5.08c.044-.026.102-.026.145 0l8.798 5.08c.046.026.074.081.074.132v11.394c0 .051-.028.106-.074.132l-8.798 5.08c-.043.026-.101.026-.144 0l-2.248-1.336c-.064-.037-.144-.04-.21-.011-.55.307-.658.373-1.177.45-.12.019-.301.06.073.276l2.93 1.738c.23.133.49.202.772.202s.542-.069.772-.202l8.798-5.08c.476-.27.772-.772.772-1.27V6.32c0-.518-.296-.999-.772-1.27L12.77.202C12.538.07 12.268 0 11.998 0zm2.657 6.343c-2.432 0-2.945.953-2.945 2.146 0 .145.117.263.263.263h.788c.131 0 .24-.095.261-.221.177-.718.708-1.08 1.633-1.08.738 0 1.177.168 1.177.803 0 .325-.128.567-.678.73l-1.69.419c-.899.223-1.47.756-1.47 1.636 0 1.076.905 1.715 2.423 1.715 1.704 0 2.55-.593 2.656-1.866.006-.073-.018-.144-.066-.197-.047-.053-.114-.083-.186-.083h-.791c-.123 0-.23.089-.258.207-.286.644-.98.849-1.817.849-.65 0-1.16-.207-1.16-.725 0-.325.144-.424.903-.609l1.476-.367c.898-.223 1.462-.72 1.462-1.613 0-1.12-.937-1.787-2.574-1.787z'/%3E%3C/svg%3E", - TypeScript: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%233178c6' d='M1.125 0C.502 0 0 .502 0 1.125v21.75C0 23.498.502 24 1.125 24h21.75c.623 0 1.125-.502 1.125-1.125V1.125C24 .502 23.498 0 22.875 0zm17.363 9.75c.612 0 1.154.037 1.627.111.472.074.914.187 1.323.34v2.458c-.444-.223-.935-.39-1.473-.501-.539-.111-1.09-.167-1.655-.167-.562 0-1.011.062-1.349.187-.338.124-.507.335-.507.632 0 .234.095.42.285.558.19.138.503.275.94.411l1.503.434c.915.262 1.577.609 1.984 1.04.408.432.612.998.612 1.699 0 .915-.35 1.638-1.05 2.168-.7.53-1.667.795-2.9.795-.591 0-1.178-.051-1.76-.153-.582-.102-1.13-.258-1.645-.468v-2.503c.544.287 1.09.507 1.637.66.546.153 1.084.23 1.613.23.609 0 1.071-.073 1.386-.219.315-.146.472-.369.472-.669 0-.262-.106-.471-.318-.628-.212-.157-.551-.306-1.017-.447l-1.42-.395c-.877-.234-1.515-.563-1.916-.985-.4-.422-.6-.98-.6-1.673 0-.857.348-1.545 1.044-2.063.696-.518 1.633-.777 2.811-.777zm-13.6 1.77H8.45l-.031 4.18c0 .754-.13 1.314-.39 1.68-.26.367-.65.55-1.168.55-.286 0-.56-.037-.822-.11-.262-.074-.506-.173-.733-.297v1.818c.319.111.665.187 1.038.228.373.04.736.06 1.089.06.924 0 1.623-.247 2.097-.74.474-.494.711-1.254.711-2.28V11.52z'/%3E%3C/svg%3E", - Go: "/img/icons/golang.png", -}; - -// Get language icon based on package name -function getPackageIcon(packageName: string): string | undefined { - // If it starts with @vectorize-io, it's Node.js - if (packageName.startsWith('@vectorize-io')) { - return LANGUAGE_ICONS['Node.js']; - } - // If it ends with -go or contains go-, it's Go - if (packageName.endsWith('-go') || packageName.includes('go-')) { - return LANGUAGE_ICONS.Go; - } - // Otherwise assume Python - return LANGUAGE_ICONS.Python; -} - -// Generate color scheme from tag text using hash -function getTagColor(tag: string): any { - // Hash function to get consistent color from string - let hash = 0; - for (let i = 0; i < tag.length; i++) { - hash = tag.charCodeAt(i) + ((hash << 5) - hash); - } - - // 12 vibrant color palettes with better contrast - const palettes = [ - { h: 340, s: 75, l: 50 }, // Pink - { h: 291, s: 65, l: 45 }, // Purple - { h: 262, s: 55, l: 48 }, // Deep Purple - { h: 231, s: 50, l: 50 }, // Indigo - { h: 207, s: 80, l: 50 }, // Blue - { h: 199, s: 85, l: 45 }, // Light Blue - { h: 187, s: 70, l: 45 }, // Cyan - { h: 174, s: 70, l: 50 }, // Teal - { h: 142, s: 65, l: 45 }, // Green - { h: 88, s: 55, l: 48 }, // Light Green - { h: 38, s: 85, l: 50 }, // Orange - { h: 14, s: 85, l: 50 }, // Deep Orange - ]; - - const palette = palettes[Math.abs(hash) % palettes.length]; - const { h, s, l } = palette; - - return { - // Light mode: subtle background, darker text for contrast - bg: `hsla(${h}, ${s}%, ${l}%, 0.15)`, - text: `hsl(${h}, ${Math.min(s + 10, 90)}%, ${Math.max(l - 25, 25)}%)`, - border: `hsla(${h}, ${s}%, ${l}%, 0.35)`, - // Dark mode: more vibrant background, lighter text - bgDark: `hsla(${h}, ${Math.max(s - 10, 50)}%, ${l}%, 0.25)`, - textDark: `hsl(${h}, ${Math.max(s - 15, 40)}%, ${Math.min(l + 35, 85)}%)`, - borderDark: `hsla(${h}, ${s}%, ${l}%, 0.4)`, - }; -} - -export default function RecipeCarousel({ title, items }: RecipeCarouselProps): React.ReactElement { - // Generate ID from title for anchor links - const sectionId = title.toLowerCase().replace(/\s+/g, '-'); - - return ( -
-

{title}

-
-
- {items.map((item, index) => { - // Get topic color for card border - const topicColors = item.tags?.topic ? getTagColor(item.tags.topic) : null; - - return ( - -
- {item.title} - {item.description && ( -

{item.description}

- )} -
-
- {item.tags && ( -
- {item.tags.sdk && (() => { - const colors = getTagColor(item.tags.sdk); - const icon = getPackageIcon(item.tags.sdk); - return ( - - {icon && ( - - )} - {item.tags.sdk} - - ); - })()} - {item.tags.topic && (() => { - const colors = getTagColor(item.tags.topic); - return ( - - {item.tags.topic} - - ); - })()} -
- )} - β†’ -
- - ); - })} -
-
-
- ); -} diff --git a/hindsight-docs/src/components/SkillBanner.module.css b/hindsight-docs/src/components/SkillBanner.module.css index b92988cf..b6e519be 100644 --- a/hindsight-docs/src/components/SkillBanner.module.css +++ b/hindsight-docs/src/components/SkillBanner.module.css @@ -92,6 +92,43 @@ display: block; } +.titleRow { + display: flex; + align-items: center; + gap: 8px; +} + +.copyPageButton { + display: flex; + align-items: center; + gap: 4px; + background: rgba(255, 255, 255, 0.12); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 4px; + padding: 3px 7px; + color: rgba(255, 255, 255, 0.85); + font-size: 10px; + font-weight: 600; + cursor: pointer; + white-space: nowrap; + flex-shrink: 0; + transition: all 0.2s ease; +} + +.copyPageButton:hover { + background: rgba(255, 255, 255, 0.22); + color: white; +} + +.copyPageButton:active { + transform: scale(0.95); +} + +.copyPageButton.copyPageCopied { + background: rgba(255, 255, 255, 0.22); + color: white; +} + /* Dark mode - make it stand out even more */ html[data-theme='dark'] .banner { box-shadow: 0 4px 12px rgba(0, 116, 217, 0.2), diff --git a/hindsight-docs/src/components/SkillBanner.tsx b/hindsight-docs/src/components/SkillBanner.tsx index 004ce1f7..f7fae2e2 100644 --- a/hindsight-docs/src/components/SkillBanner.tsx +++ b/hindsight-docs/src/components/SkillBanner.tsx @@ -1,39 +1,138 @@ -import React, { useState } from 'react'; +import React, { useState, useCallback } from 'react'; import styles from './SkillBanner.module.css'; +function extractMarkdown(element: Element): string { + let text = ''; + + const processNode = (node: Node): string => { + if (node.nodeType === Node.TEXT_NODE) { + return node.textContent || ''; + } + if (node.nodeType === Node.ELEMENT_NODE) { + const el = node as Element; + const tagName = el.tagName.toLowerCase(); + const children = Array.from(el.childNodes).map(processNode).join(''); + switch (tagName) { + case 'h1': return `# ${children}\n\n`; + case 'h2': return `## ${children}\n\n`; + case 'h3': return `### ${children}\n\n`; + case 'h4': return `#### ${children}\n\n`; + case 'h5': return `##### ${children}\n\n`; + case 'h6': return `###### ${children}\n\n`; + case 'p': return `${children}\n\n`; + case 'ul': return `${children}\n`; + case 'ol': return `${children}\n`; + case 'li': { + const parent = el.parentElement; + const isOrdered = parent?.tagName.toLowerCase() === 'ol'; + if (isOrdered) { + const index = Array.from(parent?.children || []).indexOf(el) + 1; + return `${index}. ${children}\n`; + } + return `- ${children}\n`; + } + case 'code': { + const isBlock = el.parentElement?.tagName.toLowerCase() === 'pre'; + if (isBlock) { + const lang = el.className.replace('language-', ''); + return `\`\`\`${lang}\n${children}\n\`\`\`\n\n`; + } + return `\`${children}\``; + } + case 'pre': return children; + case 'blockquote': return children.split('\n').map(line => `> ${line}`).join('\n') + '\n\n'; + case 'a': return `[${children}](${el.getAttribute('href') || ''})`; + case 'strong': case 'b': return `**${children}**`; + case 'em': case 'i': return `*${children}*`; + case 'br': return '\n'; + case 'hr': return '---\n\n'; + case 'table': return `${children}\n`; + case 'thead': case 'tbody': return children; + case 'tr': return `${children}|\n`; + case 'th': case 'td': return `| ${children} `; + case 'img': return `![${el.getAttribute('alt') || ''}](${el.getAttribute('src') || ''})`; + default: return children; + } + } + return ''; + }; + + Array.from(element.childNodes).forEach(node => { text += processNode(node); }); + return text; +} + export default function SkillBanner(): JSX.Element { - const [copied, setCopied] = useState(false); + const [commandCopied, setCommandCopied] = useState(false); + const [pageCopied, setPageCopied] = useState(false); const command = 'npx skills add https://github.com/vectorize-io/hindsight --skill hindsight-docs'; - const handleCopy = async () => { + const handleCopyCommand = async () => { try { await navigator.clipboard.writeText(command); - setCopied(true); - setTimeout(() => setCopied(false), 2000); + setCommandCopied(true); + setTimeout(() => setCommandCopied(false), 2000); } catch (err) { console.error('Failed to copy:', err); } }; + const handleCopyPage = useCallback(async () => { + try { + const contentElement = document.querySelector('.markdown'); + if (!contentElement) return; + const title = document.querySelector('h1')?.textContent; + let markdown = title ? `# ${title}\n\n` : ''; + const contentToCopy = Array.from(contentElement.children) + .filter(child => !(child.tagName === 'H1' && child.textContent === title)) + .map(child => extractMarkdown(child)) + .join(''); + markdown += contentToCopy; + markdown = markdown.replace(/\n{3,}/g, '\n\n').trim(); + await navigator.clipboard.writeText(markdown); + setPageCopied(true); + setTimeout(() => setPageCopied(false), 2000); + } catch (error) { + console.error('Failed to copy page content:', error); + } + }, []); + return (
πŸ€–
-
- Using a coding agent? Install the docs skill for instant access +
+
+ Using a coding agent? Run this to install the Hindsight docs skill: +
+
- - {command} - + {command}