{title}
@@ -244,6 +245,8 @@ export const Article = ({question, glossary}: ArticleProps) => {
diff --git a/app/components/ArticleKeepGoing/index.tsx b/app/components/ArticleKeepGoing/index.tsx
deleted file mode 100644
index 6129fa77..00000000
--- a/app/components/ArticleKeepGoing/index.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import React, {ReactNode} from 'react'
-import Button from '../Button'
-import ListTable from '~/components/Table'
-import './keepGoing.css'
-export interface Article {
- title: string
- pageid: string
-}
-export interface NextArticle {
- title: string
- pageid: string
- icon: any
-}
-export interface ArticleKeepGoingProps {
- /**
- * Category of the article
- */
- category: string
- /**
- * Related articles
- */
- articles: Article[]
- /**
- * Next article
- */
- next: NextArticle
-}
-export const ArticleKeepGoing = ({category, articles, next}: ArticleKeepGoingProps) => {
- const nextArticle = (pageId) => {
- console.log('Next article')
- location.href = `/${pageId}`
- }
- return (
-
toc: useObjectsType
}
-const CachedObjectsContext = createContext(null)
+export const CachedObjectsContext = createContext(null)
const getGlossary = async () => (await fetchGlossary()).data
const getTags = async () => (await fetchTags()).tags
@@ -40,7 +40,6 @@ export const CachedObjectsProvider = ({children}: {children: ReactElement}) => {
const tags = useItemsFuncs(getTags)
const toc = useItemsFuncs(getToC)
- console.log('caching')
return (
{children}
diff --git a/app/hooks/useQuestionStateInUrl.ts b/app/hooks/useQuestionStateInUrl.ts
index 57d6fa1f..a3238b4a 100644
--- a/app/hooks/useQuestionStateInUrl.ts
+++ b/app/hooks/useQuestionStateInUrl.ts
@@ -1,6 +1,6 @@
import {useState, useRef, useEffect, useMemo, useCallback} from 'react'
import {useSearchParams, useNavigation} from '@remix-run/react'
-import {Question, QuestionState, RelatedQuestions, PageId, Glossary} from '~/server-utils/stampy'
+import {Question, QuestionState, RelatedQuestion, PageId, Glossary} from '~/server-utils/stampy'
import {fetchAllQuestionsOnSite} from '~/routes/questions.allQuestionsOnSite'
import {fetchGlossary} from '~/routes/questions.glossary'
import {
@@ -136,7 +136,7 @@ export default function useQuestionStateInUrl(minLogo: boolean, initialQuestions
const unshownRelatedQuestions = (
questions: Question[],
questionProps: Question
- ): RelatedQuestions => {
+ ): RelatedQuestion[] => {
const {relatedQuestions} = questionProps
const onSiteQuestions = onSiteQuestionsRef.current
diff --git a/app/hooks/useToC.tsx b/app/hooks/useToC.tsx
index cc5730bb..9e28c014 100644
--- a/app/hooks/useToC.tsx
+++ b/app/hooks/useToC.tsx
@@ -5,6 +5,7 @@ const identity = (i: any) => i
const useToC = () => {
const {items: toc} = useCachedToC()
+ console.log(toc)
const checkPath = (pageid: string) => (item: TOCItem) => {
if (item.pageid === pageid) return [pageid]
@@ -21,10 +22,32 @@ const useToC = () => {
return toc.map(checkPath(pageid)).filter(identity)[0]
}
+ const getNext = (pageid: string): TOCItem | undefined => {
+ type NextItem = {
+ current?: string
+ next?: TOCItem
+ }
+ const findNext = (prev: string | undefined, item: TOCItem): NextItem => {
+ if (pageid === prev) return {current: prev, next: item}
+
+ let previous: string | undefined = item.pageid
+ for (const child of item.children || []) {
+ const {next, current} = findNext(previous, child)
+ if (next) return {next, current}
+ previous = current
+ }
+ return {current: previous}
+ }
+
+ const all = {pageid: '', children: toc || []} as TOCItem
+ return toc && findNext('', all).next
+ }
+
return {
toc: toc || [],
findSection,
getPath,
+ getNext,
}
}
diff --git a/app/routes/questions.add.tsx b/app/routes/questions.add.tsx
index b8b2d6f8..4dfa6748 100644
--- a/app/routes/questions.add.tsx
+++ b/app/routes/questions.add.tsx
@@ -2,9 +2,9 @@ import {useState, useEffect} from 'react'
import type {ActionFunctionArgs} from '@remix-run/cloudflare'
import {Form} from '@remix-run/react'
import {redirect} from '@remix-run/cloudflare'
-import {addQuestion, loadAllQuestions, fetchJsonList, RelatedQuestions} from '~/server-utils/stampy'
+import {addQuestion, loadAllQuestions, fetchJsonList, RelatedQuestion} from '~/server-utils/stampy'
-const getRelated = async (question: string): Promise => {
+const getRelated = async (question: string): Promise => {
const url = `${NLP_SEARCH_ENDPOINT}/api/search?query=${question}?status=all`
try {
return await fetchJsonList(url)
@@ -41,7 +41,7 @@ export const action = async ({request}: ActionFunctionArgs) => {
} else {
relatedQuestions = formData
.getAll('relatedQuestion')
- .map((question) => ({title: question})) as RelatedQuestions
+ .map((question) => ({title: question})) as RelatedQuestion[]
}
// Make sure the question is formatted as a question
diff --git a/app/routes/tags.single.$tag.tsx b/app/routes/tags.single.$tag.tsx
index 0193167a..9d88e299 100644
--- a/app/routes/tags.single.$tag.tsx
+++ b/app/routes/tags.single.$tag.tsx
@@ -1,7 +1,7 @@
import {useState, useEffect, ReactNode} from 'react'
import {LoaderFunction} from '@remix-run/cloudflare'
import {reloadInBackgroundIfNeeded} from '~/server-utils/kv-cache'
-import {Tag as TagType, QuestionState, RelatedQuestions, loadTag} from '~/server-utils/stampy'
+import {Tag as TagType, QuestionState, RelatedQuestion, loadTag} from '~/server-utils/stampy'
import Dialog from '~/components/dialog'
type Props = {
@@ -47,7 +47,7 @@ export function Tag({
showCount,
}: {
name: string
- questions?: RelatedQuestions
+ questions?: RelatedQuestion[]
showCount?: boolean
}) {
const [questions, setQuestions] = useState(tqs)
diff --git a/app/server-utils/stampy.ts b/app/server-utils/stampy.ts
index dad1eeb1..ab7c1c5c 100644
--- a/app/server-utils/stampy.ts
+++ b/app/server-utils/stampy.ts
@@ -26,7 +26,7 @@ export enum QuestionState {
COLLAPSED = '-',
RELATED = 'r',
}
-export type RelatedQuestions = {title: string; pageid: string}[]
+export type RelatedQuestion = {title: string; pageid: string}
export enum QuestionStatus {
WITHDRAWN = 'Withdrawn',
SKETCH = 'Bulletpoint sketch',
@@ -59,7 +59,7 @@ export type Tag = {
name: string
url: string
internal: boolean
- questions: RelatedQuestions
+ questions: RelatedQuestion[]
mainQuestion: string | null
}
export type Question = {
@@ -67,7 +67,7 @@ export type Question = {
pageid: string
text: string | null
answerEditLink: string | null
- relatedQuestions: RelatedQuestions
+ relatedQuestions: RelatedQuestion[]
questionState?: QuestionState
tags: string[]
banners: Banner[]
@@ -82,7 +82,7 @@ export type Question = {
export type PageId = Question['pageid']
export type NewQuestion = {
title: string
- relatedQuestions: RelatedQuestions
+ relatedQuestions: RelatedQuestion[]
source?: string
}
type Entity = {
@@ -434,7 +434,7 @@ export const insertRows = async (table: string, rows: NewQuestion[]) => {
return await sendToCoda(url, payload, 'POST', `${CODA_INCOMING_TOKEN}`)
}
-export const addQuestion = async (title: string, relatedQuestions: RelatedQuestions) => {
+export const addQuestion = async (title: string, relatedQuestions: RelatedQuestion[]) => {
return await insertRows(INCOMING_QUESTIONS_TABLE, [{title, relatedQuestions}])
}
diff --git a/stories/ArticlesKeepGoing.stories.tsx b/stories/ArticlesKeepGoing.stories.tsx
index 2e35a093..29f4ab6d 100644
--- a/stories/ArticlesKeepGoing.stories.tsx
+++ b/stories/ArticlesKeepGoing.stories.tsx
@@ -1,32 +1,110 @@
import type {Meta, StoryObj} from '@storybook/react'
-import {ArticleKeepGoing} from '../app/components/ArticleKeepGoing'
-import SvgArrowRight from '../app/components/icons-generated/ArrowRight'
-import {ArticlesNav} from '~/components/ArticlesNav/Menu'
+import KeepGoing from '../app/components/Article/KeepGoing'
+import {CachedObjectsContext} from '../app/hooks/useCachedObjects'
+import type {TOCItem} from '../app/routes/questions.toc'
+import type {Question} from '../app/server-utils/stampy'
+
+const toc = {
+ title: 'New to AI safety? Start here.',
+ pageid: '9OGZ',
+ hasText: true,
+ category: 'Your momma',
+ children: [
+ {
+ title: 'What would an AGI be able to do?',
+ pageid: 'NH51',
+ hasText: false,
+ },
+ {
+ title: 'Types of AI',
+ pageid: 'NH50',
+ hasText: false,
+ children: [
+ {
+ title: 'What are the differences between AGI, transformative AI, and superintelligence?',
+ pageid: '5864',
+ hasText: true,
+ },
+ {
+ title: 'What is intelligence?',
+ pageid: '6315',
+ hasText: true,
+ },
+ ],
+ },
+ {
+ title: 'Introduction to ML',
+ pageid: 'NH50',
+ hasText: false,
+ children: [
+ {
+ title: 'What are large language models?',
+ pageid: '8161',
+ hasText: true,
+ },
+ {
+ title: 'What is compute?',
+ pageid: '9358',
+ hasText: true,
+ },
+ ],
+ },
+ ],
+} as any as TOCItem
+
+const withMockedToC = (StoryFn: any) => {
+ return (
+
+
+
+ )
+}
const meta = {
- title: 'Components/ArticleKeepGoing',
- component: ArticleKeepGoing,
+ title: 'Components/Article/KeepGoing',
+ component: KeepGoing,
tags: ['autodocs'],
-} satisfies Meta
+ decorators: [withMockedToC],
+} satisfies Meta
export default meta
-type Story = StoryObj
+type Story = StoryObj
-export const Primary: Story = {
+export const Default: Story = {
args: {
- category: 'AI alignment',
- articles: [
- {title: 'What is AI alignment', pageid: '1231', hasIcon: true},
- {title: 'What is this', pageid: '1232', hasIcon: true},
- {title: 'What is AI safety', pageid: '1233', hasIcon: true},
- {title: 'What is that', pageid: '1234', hasIcon: true},
- {title: 'What is the the orthogonality thesis', pageid: '1235', hasIcon: true},
- {title: 'What is something else', pageid: '1236', hasIcon: true},
+ pageid: 'NH50',
+ relatedQuestions: [
+ {pageid: '1412', title: 'something or other'},
+ {pageid: '1234', title: 'Another related question'},
+ {pageid: '1235', title: 'How about this one?'},
+ {pageid: '1236', title: 'What time is it?'},
],
- next: {
- title:
- 'Are there any AI alignment projects which governments could usefully put a very large amount of resources into?',
- pageid: '1235',
- icon: ,
- },
- },
+ } as any as Question,
+}
+
+export const NoMore: Story = {
+ args: {
+ pageid: '123',
+ relatedQuestions: [],
+ } as any as Question,
+}
+
+export const OnlyRelated: Story = {
+ args: {
+ pageid: '123',
+ relatedQuestions: [
+ {pageid: '1412', title: 'something or other'},
+ {pageid: '1234', title: 'Another related question'},
+ {pageid: '1235', title: 'How about this one?'},
+ {pageid: '1236', title: 'What time is it?'},
+ ],
+ } as any as Question,
+}
+
+export const OnlyNext: Story = {
+ args: {
+ pageid: 'NH50',
+ relatedQuestions: [],
+ } as any as Question,
}
-
-
- )
-}
diff --git a/app/components/Table/index.tsx b/app/components/Table/index.tsx
index e4231cf3..9b3f65d0 100644
--- a/app/components/Table/index.tsx
+++ b/app/components/Table/index.tsx
@@ -2,11 +2,16 @@ import {Link} from '@remix-run/react'
import {ArrowUpRight} from '~/components/icons-generated'
import './listTable.css'
+export type ListItem = {
+ pageid: string
+ title: string
+ hasIcon?: boolean
+}
export type ListTableProps = {
/**
* Browse by category
*/
- elements: any[]
+ elements: ListItem[]
}
export const ListTable = ({elements}: ListTableProps) => (
diff --git a/app/hooks/stateModifiers.tsx b/app/hooks/stateModifiers.tsx
index 4bc3909f..5a3fe5d7 100644
--- a/app/hooks/stateModifiers.tsx
+++ b/app/hooks/stateModifiers.tsx
@@ -1,4 +1,4 @@
-import {Question, QuestionState, RelatedQuestions, PageId} from '~/server-utils/stampy'
+import {Question, QuestionState, RelatedQuestion, PageId} from '~/server-utils/stampy'
type StateEntry = [PageId, QuestionState]
type StateString = string
@@ -70,7 +70,7 @@ export const insertAfter = (state: StateString, pageId: PageId, to: PageId): Sta
export const insertInto = (
state: StateString,
pageid: PageId,
- relatedQuestions: RelatedQuestions,
+ relatedQuestions: RelatedQuestion[],
options = {toggle: true}
): StateString =>
processStateEntries(state, (entries: StateEntry[]) =>
diff --git a/app/hooks/useCachedObjects.tsx b/app/hooks/useCachedObjects.tsx
index 1a6d373c..6f3b5f6a 100644
--- a/app/hooks/useCachedObjects.tsx
+++ b/app/hooks/useCachedObjects.tsx
@@ -29,7 +29,7 @@ type useCachedObjectsType = {
tags: useObjectsTypeKeep going! 👉
- Continue with the next article in {category} -
- {next.title}
-
-
- Or, jump to related questions
-