Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ Same goes for feature requests.
PR's are welcome, too!
Note that opening a PR means agreeing that your code becomes distributed under the MIT license.

If you want to share some thoughts on the Atomic Data _specification_, please [drop an issue in the Atomic Data docs repo](https://github.com/ontola/atomic-data/issues).
If you want to share some thoughts on the Atomic Data _specification_, please [drop an issue in the Atomic Data repo](https://github.com/atomicdata-dev/atomic-server/issues).
Check out the [Roadmap](https://docs.atomicdata.dev/roadmap.html) if you want to learn more about our plans and the history of the project.

## Table of contents

- [Table of contents](#table-of-contents)
- [Translation \& Internationalization](#translation--internationalization)
- [Running \& compiling](#running--compiling)
- [Running locally (with local development browser)](#running-locally-with-local-development-browser)
- [IDE setup (VSCode)](#ide-setup-vscode)
Expand All @@ -39,6 +40,11 @@ Check out the [Roadmap](https://docs.atomicdata.dev/roadmap.html) if you want to
- [Deploying to atomicdata.dev](#deploying-to-atomicdatadev)
- [Publishing atomic-cli to WAPM](#publishing-atomic-cli-to-wapm)

## Translation & Internationalization

AtomicServer supports a small number of languages.
Most of these translations are done by AI and might contain mistakes, if you notice any feel free to [open an issue](https://github.com/atomicdata-dev/atomic-server/issues).

## Running & compiling

TL;DR Clone the repo and run `cargo run` from each folder (e.g. `cli` or `server`).
Expand Down Expand Up @@ -124,7 +130,7 @@ cargo nextest run test_name_substring
# First, run the server
cargo run
# now, open new terminal window
cd server/e2e_tests/ && npm i && npm run test
cd browser && pnpm i && pnpm test-e2e
# if things go wrong, debug!
pnpm run test-query {testname}
```
Expand Down
1 change: 1 addition & 0 deletions browser/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This changelog covers all five packages, as they are (for now) updated as a whol
- [#1008](https://github.com/atomicdata-dev/atomic-server/issues/1008) Add info dropdowns to different sections of the ontology editor for more information about the section.
- [#459](https://github.com/atomicdata-dev/atomic-server/issues/459) New feature: Add tags to your resources to better organize your data. Search for resources with specific tags in the search bar with `tag:[name]`.
- [#951](https://github.com/atomicdata-dev/atomic-server/issues/951) New feature: Atomic Assistant, AI chat interface with support for custom agents, MCP servers and more. Bring your own OpenRouter key or use Ollama to host your own models.
- [#1118](https://github.com/atomicdata-dev/atomic-server/issues/1118) New feature: AtomicServer is now also available in German, Spanish and French. Change your language on the settings page.

### @tomic/lib

Expand Down
11 changes: 7 additions & 4 deletions browser/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ Vite hosts the data-browser and targets `.ts` files which enables hot reload / h
If you're editing `@tomic/lib` or `@tomic/react`, you need to re-build the library, as `atomic-data-browser` imports the `.js` files.
You can auto re-build using the `watch` commands in `@tomic/lib` and `@tomic/react`.
If you run `pnpm start` from the root, these will be run automatically.
Note that you may need to refresh your screen manually to show updates from these libraries.

There are two possible solutions for improving this workflow:
## Localization

- In `package.json` of the libraries, set the `main` to `src/index.ts` (the typescript file). However, make sure to _not_ publish this to npm, as many clients will fail.
- Properly set up aliases with vite. I've tried this before, but failed.
Atomic Data Browser uses [Wuchale](https://wuchale.dev/) for localization.
When adding new text to the app wuchale will automatically extract it and add it to the locale files (When running the vite dev server).
Make sure you provide translations for the any new text you add.
To help with this you can provide a Google Gemini API key, Wuchale will then use this to generate translations for you automatically.
To do so export the key in your terminal or use something like direnv to set the key: `export GEMINI_API_KEY=your_api_key`
More info: [How to use Gemini live translation](https://wuchale.dev/guides/gemini/)
6 changes: 5 additions & 1 deletion browser/data-browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
"stylis": "4.3.0",
"tippy.js": "^6.3.7",
"tiptap-markdown": "^0.8.10",
"wuchale": "^0.16.5",
"@wuchale/jsx": "^0.7.4",
"@wuchale/vite-plugin": "^0.14.6",
"zod": "^4.1.5"
},
"devDependencies": {
Expand Down Expand Up @@ -105,6 +108,7 @@
"preview": "vite preview",
"start": "vite",
"test": "vitest run",
"typecheck": "pnpm exec tsc --noEmit"
"typecheck": "pnpm exec tsc --noEmit",
"clean-translations": "pnpm exec wuchale --clean"
}
}
77 changes: 40 additions & 37 deletions browser/data-browser/src/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { NavStateProvider } from './components/NavState';
import { Toaster } from './components/Toaster';
import { McpServersProvider } from './components/AI/MCP/useMcpServers';
import { AISettingsContextProvider } from '@components/AI/AISettingsContext';
import { LocaleProvider } from '@components/LocaleContext';

// Setup bugsnag for error handling, but only if there's an API key
const ErrBoundary = window.bugsnagApiKey
Expand All @@ -45,43 +46,45 @@ const shouldForwardProp: ShouldForwardProp<'web'> = (propName, target) => {
export const Providers: React.FC<React.PropsWithChildren> = ({ children }) => {
return (
<NavStateProvider>
<AppSettingsContextProvider>
<AISettingsContextProvider>
<McpServersProvider>
<ControlLockProvider>
<HotKeysWrapper>
<StyleSheetManager shouldForwardProp={shouldForwardProp}>
<ThemeWrapper>
<GlobalStyle />
<ErrBoundary FallbackComponent={CrashPage}>
{/* Default form validation provider. Does not do anything on its own but will make sure useValidation works without context*/}
<FormValidationContextProvider
onValidationChange={() => undefined}
>
<Toaster />
<MetaSetter />
<DropdownContainer>
<DialogGlobalContextProvider>
<PopoverContainer>
<DropdownContainer>
<NewResourceUIProvider>
<SkipNav />
<NavWrapper>{children}</NavWrapper>
</NewResourceUIProvider>
</DropdownContainer>
</PopoverContainer>
<NetworkIndicator />
</DialogGlobalContextProvider>
</DropdownContainer>
</FormValidationContextProvider>
</ErrBoundary>
</ThemeWrapper>
</StyleSheetManager>
</HotKeysWrapper>
</ControlLockProvider>
</McpServersProvider>
</AISettingsContextProvider>
</AppSettingsContextProvider>
<LocaleProvider>
<AppSettingsContextProvider>
<AISettingsContextProvider>
<McpServersProvider>
<ControlLockProvider>
<HotKeysWrapper>
<StyleSheetManager shouldForwardProp={shouldForwardProp}>
<ThemeWrapper>
<GlobalStyle />
<ErrBoundary FallbackComponent={CrashPage}>
{/* Default form validation provider. Does not do anything on its own but will make sure useValidation works without context*/}
<FormValidationContextProvider
onValidationChange={() => undefined}
>
<Toaster />
<MetaSetter />
<DropdownContainer>
<DialogGlobalContextProvider>
<PopoverContainer>
<DropdownContainer>
<NewResourceUIProvider>
<SkipNav />
<NavWrapper>{children}</NavWrapper>
</NewResourceUIProvider>
</DropdownContainer>
</PopoverContainer>
<NetworkIndicator />
</DialogGlobalContextProvider>
</DropdownContainer>
</FormValidationContextProvider>
</ErrBoundary>
</ThemeWrapper>
</StyleSheetManager>
</HotKeysWrapper>
</ControlLockProvider>
</McpServersProvider>
</AISettingsContextProvider>
</AppSettingsContextProvider>
</LocaleProvider>
</NavStateProvider>
);
};
2 changes: 1 addition & 1 deletion browser/data-browser/src/chunks/AI/AgentConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const defaultAgents: AIAgent[] = [
id: 'dev.atomicdata.atomic-agent',
description:
"An agent that is specialized in helping you use AtomicServer. It takes context from what you're doing.",
systemPrompt: `You are an AI assistant in the Atomic Data Browser. Users will ask questions about their data and you will answer by looking at the data or using your own knowledge about the world.
systemPrompt: /* wc-ignore */ `You are an AI assistant in the Atomic Data Browser. Users will ask questions about their data and you will answer by looking at the data or using your own knowledge about the world.
Atomic Data uses JSON-AD, Every resource including the properties themselves have a subject (the '@id' property in the JSON-AD), this is a URL that points to the resource.
Resources are always referenced by subject so make sure you have all the subjects you need before editing or creating resources.

Expand Down
1 change: 1 addition & 0 deletions browser/data-browser/src/chunks/AI/atomicSchemaHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @wc-ignore-file
import { type Core, type Store } from '@tomic/react';

export const toClassString = async (subject: string, store: Store) => {
Expand Down
6 changes: 3 additions & 3 deletions browser/data-browser/src/chunks/AI/useAgentAutoSelect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const useAutoAgentSelect = () => {

const getModel = useGetModel();

const basePrompt = `You are part of an AI Chat application. It is your job to determine what agent to use to answer the users question.
const basePrompt = /* @wc-ignore */ `You are part of an AI Chat application. It is your job to determine what agent to use to answer the users question.
These are the agents you can choose from:

${agents.map(agent => agentToText(agent, mcpServers)).join('\n')}
Expand All @@ -35,8 +35,8 @@ User question: `;

const { object } = await generateObject({
model,
schemaName: 'Agent',
schemaDescription: 'The agent to use for the question.',
schemaName: /* @wc-ignore */ 'Agent',
schemaDescription: /* @wc-ignore */ 'The agent to use for the question.',
schema: z.object({
agentId: z.string(),
}),
Expand Down
1 change: 1 addition & 0 deletions browser/data-browser/src/chunks/AI/useAtomicTools.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @wc-ignore-file
import {
commits,
core,
Expand Down
1 change: 1 addition & 0 deletions browser/data-browser/src/chunks/AI/useGenerativeData.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @wc-ignore-file
import { generateObject, generateText } from 'ai';
import { type AtomicUIMessage } from './types';
import z from 'zod';
Expand Down
1 change: 1 addition & 0 deletions browser/data-browser/src/chunks/AI/useProcessMessages.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @wc-ignore-file
import { commits, useStore, type Resource, type Store } from '@tomic/react';
import { type AIMessageContext, type AtomicUIMessage } from './types';
import { toClassString } from './atomicSchemaHelpers';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @wc-ignore-file
/**
* Function to map currency codes to names using this list: https://www.six-group.com/dam/download/financial-information/data-center/iso-currrency/lists/list-one.xml
* Used to update the string in currencies.ts.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @wc-ignore-file
import { Position, Node } from 'reactflow';

// this helper function returns the intersection point
Expand Down
6 changes: 3 additions & 3 deletions browser/data-browser/src/components/AI/AISidebarContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ export const AISidebarContextProvider: React.FC<React.PropsWithChildren> = ({
);
};

export const newContextItem = <T extends AIMessageContext>(
export function newContextItem<T extends AIMessageContext>(
item: Omit<T, 'id'>,
): T => {
): T {
return {
...item,
id: crypto.randomUUID() as string,
} as T;
};
}
2 changes: 1 addition & 1 deletion browser/data-browser/src/components/EditableTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function EditableTitle({
setIsEditing(true);
}

const placeholder = canEdit ? 'set a title' : 'Untitled';
const placeholder = canEdit ? 'Set a title' : 'Untitled';

useEffect(() => {
ref.current?.focus();
Expand Down
1 change: 1 addition & 0 deletions browser/data-browser/src/components/HotKeyWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @wc-ignore-file
import * as React from 'react';
import { dataURL, editURL } from '../helpers/navigation';
import { useHotkeys } from 'react-hotkeys-hook';
Expand Down
47 changes: 47 additions & 0 deletions browser/data-browser/src/components/LocaleContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useLocalStorage } from '@hooks/useLocalStorage';
import React, { createContext, useContext, useEffect, useState } from 'react';
import { loadLocale } from 'wuchale/load-utils';

interface LocaleContext {
locale: string;
setLocale: (locale: string) => void;
}

const LocaleContext = createContext<LocaleContext>({
locale: 'en',
setLocale: () => {},
});

export const SUPPORTED_LOCALES = ['en', 'es', 'fr', 'de'];

export const LocaleProvider = ({ children }: React.PropsWithChildren) => {
const [locale, setLocale] = useLocalStorage(
'atomic.locale',
getBrowserLocale(),
);
const [localeLoaded, setLocaleLoaded] = useState(false);

useEffect(() => {
setLocaleLoaded(false);
loadLocale(locale).then(() => setLocaleLoaded(true));
}, [locale]);

return (
<LocaleContext.Provider value={{ locale, setLocale }}>
{/* Refresh the whole tree when changing locale */}
<React.Fragment key={localeLoaded ? 'loaded' : 'loading'}>
{children}
</React.Fragment>
</LocaleContext.Provider>
);
};

export const useLocale = () => {
return useContext(LocaleContext);
};

const getBrowserLocale = () => {
const locales = navigator.languages.map(x => x.trim().split(/-|_/)[0]);

return locales.find(x => SUPPORTED_LOCALES.includes(x)) ?? 'en';
};
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function useTagList(): TagWithTitle[] {

// Gracefully fall back to a no-op implementation if the browser doesn't support the Highlight API.
const newHighlight = () => {
if ('Highlight' in window) {
if (/* @wc-ignore */ 'Highlight' in window) {
return new window.Highlight();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ const matchModifier = (
) =>
handler.mod === undefined ||
handler.mod ===
(navigator.platform.includes('Mac') ? event.metaKey : event.ctrlKey);
(navigator.platform.includes(/* @wc-ignore */ 'Mac')
? event.metaKey
: event.ctrlKey);

const matchCondition = (handler: KeyboardHandler, context: HandlerContext) =>
handler.condition === undefined || handler.condition(context);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @wc-ignore-file
import { core, dataBrowser } from '@tomic/react';
import type { TemplateFn, TemplateContext } from '../template';

Expand Down
2 changes: 1 addition & 1 deletion browser/data-browser/src/components/forms/ResourceForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ export function ResourceForm({
<StyledCollapse open={showAdvanced}>
<Column>
<Field
label='add another property...'
label='Add another property...'
helper='In Atomic Data, any Resource could have any single Property. Use this field to add new property-value combinations to your resource.'
>
<div>
Expand Down
2 changes: 1 addition & 1 deletion browser/data-browser/src/components/forms/UploadForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function UploadForm({
<div {...getRootProps()}>
<input {...getInputProps()} />
{isDragActive ? (
<p>{'Drop the files here ...'}</p>
<p>Drop the files here ...</p>
) : (
<Button
subtle
Expand Down
Loading
Loading