Skip to content

Antikythera

When you simply need to query information about an Antikythera article, annotation or information on the Antikythera project itself (such as the settings). The library provides an object that can be used to query all that information. You simply need to instantiate the Antikythera object and call one of the following methods on it.

INFO

Please contact Antikythera to get the correct 'entry' slug for your article.

javascript
import { Antikythera } from '@antikythera/antikythera'
import '@antikythera/antikythera/fonts'
const antikythera = new Antikythera({ entry: 'example-entry' })

init()

By default, the Antikythera class automatically initializes the antikythera-menu Web Component and crawls the DOM for .annotation tagged elements.

You can override this behavior by passing the manual: true parameter to the Antikythera class and then calling the init() method manually:

javascript
const antikythera = new Antikythera({ entry: 'example-entry', manual: true })

// wait 2 seconds before initializing:
setTimeout(() => {
	antikythera.init()
}, 2000)

When manually initializing the antikythera-menu, your optional naming overrides are passed directly to the init() method:

javascript
antikythera.init({
	menuName: 'antikythera-menu', // should match the name you've specified in the Antikythera class
	annotationClass: '.annotation',
	detectAnnotationsOnInit: true // set false here if for some reason you want to call detectAnnotations() method at a later point in time
})

init() Options

NameTypeRequiredUsageDefault
menuNameStringfalseName of Antikythera Menu Web Componentantikythera-menu
annotationClassStringfalseName of Selector Class used to find inline annotations.annotation
detectAnnotationsOnInitBooleanfalseThe current body will be scanned for ${annotionClass} annotationstrue

reinit()

In case your website is dealing with multiple antikythera articles and you need to switch between them, a reinit method is provided to reinitialize all of the antikythera components. This method behaves just the same way as the init method does but accepts an additional entry argument which resets all of the inner workings to deal with this new entry instead of the original one. After reinitialisation an event will be emited which you can listen to for further processing on your website. This event is called antikythera:initComplete and is emitted on the document object.

javascript
const antikythera = new Antikythera({ entry: 'example-entry' })

document.addEventListener('antikythera:initComplete', (e) => {
	console.log('reinitialisation complete')
	console.log(e.detail.entry) // <-- latest entry
	// do further processing / cleanup here...
})

setTimeout(() => {
	antikythera.reinit('other-entry')
}, 3000)

reinit() Options

NameTypeRequiredUsageDefault
entryStringtrueName of the new antikythera entry to useundefined
menuNameStringfalseName of Antikythera Menu Web Componentantikythera-menu
annotationClassStringfalseName of Selector Class used to find inline annotations.annotation
detectAnnotationsOnInitBooleanfalseThe current body will be scanned for ${annotionClass} annotationstrue

detectAnnotations()

This method crawls the html body of the current page and scans for html span elements with a matching annotation class. It consequently applies styling to the found elements and sets up the interaction with the antikythera-menu component so that a corresponding annotation card is displayed when the article's annotation is in the viewport.

javascript
<span class="annotation">my-annotation</span>

It will be called by default in the init() method unless specified by passing in detectAnnotationsOnInit = false as discussed previously. In most cases you will probably not need to worry about all of this and not need to call this method manually. A fitting example when you should/could use it however is for example when dealing with a multi-language website where the DOM changes depending on the current language chosen. If your website is dynamically changing the article' content it might be fitting and even required to call this method.

javascript
const toggleLanguageToEnglish = async () => {
	langIsEng.value = true
	await antikythera.detectAnnotations()
}
const toggleLanguageToFrench = async () => {
	langIsEng.value = false
	await antikythera.detectAnnotations()
}

getEntry()

javascript
const article = antikythera.getEntry()

Response

Details
json
{
	"settings": {
		"externalLinks": [
			{
				"linkTitle": "instagram",
				"linkUrl": "https://www.instagram.com"
			}
		],
		"shortDescription": "Journal of Philosophy of Planetary Computation.",
		"blurb": "Journal of Philosophy of Planetary Computation."
	},
	"entry": {
		"doi": "123456789",
		"designers": [
			{
				"title": "Firstname Lastname",
				"externalLink": null,
				"featuredImage": {
					"url": null,
					"alt": null,
					"caption": null
				},
				"biography": null
			}
		],
		"featuredImage": {
			"url": "https://cdn.sanity.io/images/y9ih15q4/production/fe543c33bd7f472edd9d40b7c899e637033e609b-3000x2024.jpg",
			"alt": "a very religious scene",
			"caption": null
		},
		"credits": "Yuri Bultheel",
		"releaseDate": "2024-09-04",
		"authors": [
			{
				"title": "Philip Maughan",
				"externalLink": null,
				"featuredImage": {
					"url": null,
					"alt": null,
					"caption": null
				},
				"biography": null
			}
		],
		"annotations": [
			{
				"featuredImage": {
					"url": "https://cdn.sanity.io/images/y9ih15q4/production/fe543c33bd7f472edd9d40b7c899e637033e609b-3000x2024.jpg",
					"alt": "religious scene",
					"caption": "a painful scene"
				},
				"id": "example-annotation",
				"title": "Example Annotation",
				"doi": "example DOI",
				"content": "Just some text about a certain annotation...",
				"annotationType": "commentary",
				"externalLink": "https://www.google.com"
			},
			{
				"externalLink": "https://www.dataexcess.xyz",
				"featuredImage": {
					"url": "https://cdn.sanity.io/images/y9ih15q4/production/fe543c33bd7f472edd9d40b7c899e637033e609b-3000x2024.jpg",
					"alt": null,
					"caption": "good picture"
				},
				"id": "second-annotation",
				"title": "A second Annotation",
				"doi": null,
				"content": "This is another annotation...",
				"annotationType": "related"
			}
		],
		"title": "Example Entry",
		"introduction": "Just an introduction to the article.\n\n\n\nPart 1.\n\nSome more text.\n\nPart 2.\n\nSome more text.\n\n\n\nFinal words.",
		"bibliography": "start of bibliography.\n\n\n\nthis is actual portable text by the way....\n\n\n\nend of bibliography."
	}
}

Advanced

Parameters

By default, getEntry() returns Portable Text for any richtext content. The getEntry() method exposes an optional parameter to return a String or raw HTML instead:

javascript
const articleWithStrings = antikythera.getEntry({ textStyle: 'string' })

const articleWithHtml = antikythera.getEntry({ textStyle: 'html' })
NameTypeRequiredUsageDefault
textStyleStringfalseFormat of returned richtextportabletext

getAnnotations()

javascript
const annotations = antikythera.getAnnotations()

Side Note

The Antikythera API will only return annotations that have been added to a specific entry from within the Antikythera CMS.

Response

Details
json
[
	{
		"content": "is a systematic study of general and fundamental questions concerning topics like",
		"scanText": {
			"enableScanText": true,
			"scanSegments": [
				{
					"scanKeyword": "philosophy",
					"scanPhrase": "Sciences are born when philosophy learns to ask the right questions"
				},
				{
					"scanKeyword": "Philosophie",
					"scanPhrase": "Wissenschaften entstehen, wenn die Philosophie lernt, die richtigen Fragen zu stellen"
				}
			]
		},
		"url": "philosophy",
		"annotationType": "commentary",
		"externalLink": null,
		"doi": null,
		"title": "philosophy",
		"id": "philosophy",
		"featuredImage": {
			"url": "https://cdn.sanity.io/images/y9ih15q4/production/cc9a720edc9fec8bde756dbb6fc655195528a9bf-520x693.jpg",
			"alt": "penseur",
			"caption": ""
		}
	},
	{
		"url": "cosmology",
		"scanText": {
			"enableScanText": true,
			"scanSegments": [
				{
					"scanKeyword": "cosmology",
					"scanPhrase": "This is the planetary in planetary computation: a cosmology in every sense of the word"
				},
				{
					"scanKeyword": "Kosmologie",
					"scanPhrase": "Dies ist das Planetarische in der planetarischen Berechnung: eine Kosmologie im wahrsten Sinne des Wortes"
				}
			]
		},
		"annotationType": "commentary",
		"doi": null,
		"content": "Ancient Greek"
		"featuredImage": {
			"url": "https://cdn.sanity.io/images/y9ih15q4/production/3d5387885f63443a8bb6e31a702eb76fba04dbbd-620x541.jpg",
			"alt": null,
			"caption": ""
		},
		"externalLink": null,
		"title": "cosmology",
		"id": "cosmology"
	}
]

Advanced

Parameters

By default, getAnnotations() returns Portable Text for any richtext content. The getAnnotations() method exposes an optional parameter to return a String or raw HTML instead:

javascript
const annotationsWithStrings = antikythera.getAnnotations({ textStyle: 'string' })

const annotationsWithHtml = antikythera.getAnnotations({ textStyle: 'html' })
NameTypeRequiredUsageDefault
textStyleStringfalseFormat of returned richtextportabletext

getSettings()

javascript
const settings = antikythera.getSettings()

Response

Details
json
{
	"externalLinks": [
		{
			"linkTitle": "instagram",
			"linkUrl": "https://www.instagram.com"
		}
	],
	"shortDescription": [
		{
			"markDefs": [],
			"children": [
				{
					"_type": "span",
					"marks": [],
					"text": "A Journal of Philosophy of Planetary Computation.\n\n",
					"_key": "45fb4d058d160"
				}
			],
			"_type": "block",
			"style": "normal",
			"_key": "2d544012f8bd"
		},
		{
			"markDefs": [],
			"children": [
				{
					"marks": [],
					"text": "Coming Spring 2025",
					"_key": "d079d3f606f00",
					"_type": "span"
				}
			],
			"_type": "block",
			"style": "normal",
			"_key": "5ee97e6097ec"
		}
	],
	"blurb": "A Journal of Philosophy of Planetary Computation."
}

Advanced

Parameters

By default, getSettings() returns Portable Text for any richtext content. The getSettings() method exposes an optional parameter to return a String or raw HTML instead:

javascript
const settingsWithStrings = antikythera.getSettings({ textStyle: 'string' })

const settingsWithHtml = antikythera.getSettings({ textStyle: 'html' })
NameTypeRequiredUsageDefault
textStyleStringfalseFormat of returned richtextportabletext

Options

When creating the Antikythera class, only the entry parameter is required.

NameTypeRequiredUsageDefault
entryStringtrueEntry slug (provided by Antikythera)undefined
menuNameStringfalseName of Antikythera Menu Web Componentantikythera-menu
annotationClassStringfalseName of Selector Class used to find inline annotations.annotation
customCssBooleanfalseTurn on to disable the .annotation custom CSS.false
manualBooleanfalseOption to initialize Antikythera Web Component Manually (via the antikythera.init() method)false
detectAnnotationsOnInitBooleanfalseOption to scan for annotations in init method (as opposed to manually via the antikythera.detectAnnotations() method)true
environmentStringfalseOption to specify dataset.production

Advanced queries

The Antikythera npm package is built on top of a Sanity CMS backend. If its capabilities are insufficient, you can use the Sanity API directly to perform more complex queries. Please consult the Sanity Client Documentation to learn how to use the Sanity API. Once you have the projectId, you can query the database with the Sanity client like this:

INFO

Please contact Antikythera to get the sanity projectId.

javascript
import { createClient } from '@sanity/client'

const client = createClient({
	projectId: '${antikythera-project-id}',
	dataset: 'production',
	apiVersion: '2023-06-01',
	useCdn: false
})

const query = `{
	'entry': *[_type == "entry" && url.current == "example-entry"][0]{
		title,
		introduction,
		content[]{
			...,
			"markDefs": markDefs[] {
				...,
				"slug": @->url.current
			}
		}
	}
}`

const fetchEntry = async () => {
	try {
		const result = await client.fetch(query)
		console.log('Entry data:', result.entry)
		return result.entry
	} catch (error) {
		console.error('Error fetching entry:', error)
	}
}

const entry = await fetchEntry()