diff --git a/ui/src/components/app/sidenav.tsx b/ui/src/components/app/sidenav.tsx new file mode 100644 index 0000000..2d709bf --- /dev/null +++ b/ui/src/components/app/sidenav.tsx @@ -0,0 +1,62 @@ +import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"; +import { useEffect, useState } from 'react'; + +export const SideNav = ({logitsExist}: {logitsExist:boolean}) => { + const [activeId, setActiveId] = useState(''); + let idList + if (logitsExist){ + idList = ['Top', 'Hist.', 'Logits', 'Act.'] + } else{ + idList = ['Top', 'Hist.', 'Act.'] + } + + const handleScroll = () => { + const sections = document.querySelectorAll('div[id]'); + let currentSectionId = ''; + + sections.forEach(section => { + if (idList.indexOf(section.id) != -1){ + const rect = section.getBoundingClientRect(); + if (rect.top <= window.innerHeight / 2) { + currentSectionId = section.id; + } + } + }); + + setActiveId(currentSectionId); + }; + + useEffect(() => { + window.addEventListener('scroll', handleScroll); + + // Run the handler to set the initial active section + handleScroll(); + + return () => { + window.removeEventListener('scroll', handleScroll); + }; + }, ); + + return ( + + + + CONTENTS + + + +
+
    + {idList.map((item) => ( +
  • + + {item} + + {activeId === item &&
    } +
  • ))} +
+
+
+
+ ); +}; diff --git a/ui/src/components/feature/feature-card.tsx b/ui/src/components/feature/feature-card.tsx index fecc1a5..38cf273 100644 --- a/ui/src/components/feature/feature-card.tsx +++ b/ui/src/components/feature/feature-card.tsx @@ -88,7 +88,7 @@ export const FeatureCard = ({ feature }: { feature: Feature }) => { const [showCustomInput, setShowCustomInput] = useState(false); return ( - + @@ -108,7 +108,7 @@ export const FeatureCard = ({ feature }: { feature: Feature }) => { -
+

Activation Histogram

{
{feature.logits && ( -
+

Logits

@@ -180,10 +180,17 @@ export const FeatureCard = ({ feature }: { feature: Feature }) => {
)} -
+
- {feature.sampleGroups.map((sampleGroup) => ( + {feature.sampleGroups.slice(0,feature.sampleGroups.length/2).map((sampleGroup) => ( + + {analysisNameMap(sampleGroup.analysisName)} + + ))} + + + {feature.sampleGroups.slice(feature.sampleGroups.length/2,feature.sampleGroups.length).map((sampleGroup) => ( {analysisNameMap(sampleGroup.analysisName)} diff --git a/ui/src/components/feature/sample.tsx b/ui/src/components/feature/sample.tsx index 3ab6052..58655c0 100644 --- a/ui/src/components/feature/sample.tsx +++ b/ui/src/components/feature/sample.tsx @@ -3,6 +3,7 @@ import { SuperToken } from "./token"; import { mergeUint8Arrays } from "@/utils/array"; import { useState } from "react"; import { AppPagination } from "../ui/pagination"; +import { Accordion, AccordionTrigger, AccordionContent, AccordionItem } from "../ui/accordion"; export const FeatureSampleGroup = ({ feature, @@ -69,18 +70,71 @@ export const FeatureActivationSample = ({ sample, sampleName, maxFeatureAct }: F [0] ); + const tokensList = tokens.map((t) => t.featureAct) + const startTrigger = Math.max(tokensList.indexOf(Math.max(...tokensList)) - 100, 0); + const endTrigger = Math.min(tokensList.indexOf(Math.max(...tokensList)) + 100, sample.context.length); + const tokensTrigger = sample.context.slice(startTrigger, endTrigger).map((token, i) => ({ + token, + featureAct: sample.featureActs[startTrigger + i], + })); + + const [tokenGroupsTrigger, __] = tokensTrigger.reduce<[Token[][], Token[]]>( + ([groups, currentGroup], token) => { + const newGroup = [...currentGroup, token]; + try { + decoder.decode(mergeUint8Arrays(newGroup.map((t) => t.token))); + return [[...groups, newGroup], []]; + } catch { + return [groups, newGroup]; + } + }, + [[], []] + ); + + const tokenGroupPositionsTrigger = tokenGroupsTrigger.reduce( + (acc, tokenGroup) => { + const tokenCount = tokenGroup.length; + return [...acc, acc[acc.length - 1] + tokenCount]; + }, + [0] + ); + + return (
- {sampleName && {sampleName}: } - {tokenGroups.map((tokens, i) => ( - - ))} + + + +
+ {sampleName && {sampleName}: } + ... + {tokenGroupsTrigger.map((tokens, i) => ( +
+ +
+ ))} + ... +
+
+ + {tokenGroups.map((tokens, i) => ( + + ))} + +
+
); }; diff --git a/ui/src/components/ui/accordion.tsx b/ui/src/components/ui/accordion.tsx index bb60ae5..3068d67 100644 --- a/ui/src/components/ui/accordion.tsx +++ b/ui/src/components/ui/accordion.tsx @@ -22,7 +22,7 @@ const AccordionTrigger = React.forwardRef< svg]:rotate-180", + "flex flex-1 items-center justify-between py-4 font-medium transition-all [&[data-state=open]>svg]:rotate-180", className )} {...props} diff --git a/ui/src/globals.css b/ui/src/globals.css index 9e24275..bdca90d 100644 --- a/ui/src/globals.css +++ b/ui/src/globals.css @@ -74,3 +74,19 @@ @apply bg-background text-foreground; } } + + +html { + scroll-behavior: smooth; +} +/* Side navigation styles */ +.side-nav { + position: fixed; + right: 0; + top: 0; + width: 125px; + height: 100%; + background-color: #f4f4f4; + /* padding: 10px; */ + box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1); +} diff --git a/ui/src/routes/features/page.tsx b/ui/src/routes/features/page.tsx index f55925f..b13cbf7 100644 --- a/ui/src/routes/features/page.tsx +++ b/ui/src/routes/features/page.tsx @@ -1,5 +1,6 @@ import { AppNavbar } from "@/components/app/navbar"; import { FeatureCard } from "@/components/feature/feature-card"; +import { SideNav} from "@/components/app/sidenav"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; @@ -90,8 +91,9 @@ export const FeaturesPage = () => { return (
+
-
+
Select dictionary: