diff --git a/MODULE.bazel b/MODULE.bazel
index cad256cd..7b685eb4 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -1,7 +1,7 @@
module(
name = "claro-lang",
repo_name = "claro-lang",
- version = "0.1.409",
+ version = "0.1.499",
)
bazel_dep(name = "aspect_bazel_lib", version = "2.0.1")
diff --git a/logo/BUILD b/logo/BUILD
index 2e810d9b..34459822 100644
--- a/logo/BUILD
+++ b/logo/BUILD
@@ -1,3 +1,5 @@
+load("@aspect_rules_js//js:defs.bzl", "js_library")
+
exports_files([
"ClaroLogoFromArrivalHeptapodOfferWeapon1.jpeg",
"ClaroLogoFromArrivalHeptapodOfferWeapon-transparentBackground.png",
diff --git a/tools/clarodocs/index.html b/tools/clarodocs/index.html
index 85fea98c..adf48eef 100644
--- a/tools/clarodocs/index.html
+++ b/tools/clarodocs/index.html
@@ -2,9 +2,9 @@
-
+
-
+
- React App
+ ClaroDocs
diff --git a/tools/clarodocs/package.json b/tools/clarodocs/package.json
index 9bb803ac..1cd58d69 100644
--- a/tools/clarodocs/package.json
+++ b/tools/clarodocs/package.json
@@ -10,6 +10,7 @@
"mermaid": "10.8.0",
"react": "~18.2.0",
"react-dom": "~18.2.0",
+ "react-router-dom": "^6.22.3",
"web-vitals": "2.1.4"
},
"devDependencies": {
diff --git a/tools/clarodocs/pnpm-lock.yaml b/tools/clarodocs/pnpm-lock.yaml
index 30f5e9f6..ffe062ac 100644
--- a/tools/clarodocs/pnpm-lock.yaml
+++ b/tools/clarodocs/pnpm-lock.yaml
@@ -25,6 +25,9 @@ dependencies:
react-dom:
specifier: ~18.2.0
version: 18.2.0(react@18.2.0)
+ react-router-dom:
+ specifier: ^6.22.3
+ version: 6.22.3(react-dom@18.2.0)(react@18.2.0)
web-vitals:
specifier: 2.1.4
version: 2.1.4
@@ -2120,6 +2123,11 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /@remix-run/router@1.15.3:
+ resolution: {integrity: sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==}
+ engines: {node: '>=14.0.0'}
+ dev: false
+
/@rollup/pluginutils@5.1.0:
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
engines: {node: '>=14.0.0'}
@@ -6801,6 +6809,29 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
+ /react-router-dom@6.22.3(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ react: '>=16.8'
+ react-dom: '>=16.8'
+ dependencies:
+ '@remix-run/router': 1.15.3
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ react-router: 6.22.3(react@18.2.0)
+ dev: false
+
+ /react-router@6.22.3(react@18.2.0):
+ resolution: {integrity: sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ react: '>=16.8'
+ dependencies:
+ '@remix-run/router': 1.15.3
+ react: 18.2.0
+ dev: false
+
/react@18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'}
diff --git a/tools/clarodocs/public/claro-logo-192x192.png b/tools/clarodocs/public/claro-logo-192x192.png
new file mode 100644
index 00000000..1f956ad9
Binary files /dev/null and b/tools/clarodocs/public/claro-logo-192x192.png differ
diff --git a/tools/clarodocs/public/claro-logo-512x512.png b/tools/clarodocs/public/claro-logo-512x512.png
new file mode 100644
index 00000000..7729acc6
Binary files /dev/null and b/tools/clarodocs/public/claro-logo-512x512.png differ
diff --git a/tools/clarodocs/public/claro-logo.ico b/tools/clarodocs/public/claro-logo.ico
new file mode 100644
index 00000000..fd54862e
Binary files /dev/null and b/tools/clarodocs/public/claro-logo.ico differ
diff --git a/tools/clarodocs/public/favicon.ico b/tools/clarodocs/public/favicon.ico
deleted file mode 100644
index a11777cc..00000000
Binary files a/tools/clarodocs/public/favicon.ico and /dev/null differ
diff --git a/tools/clarodocs/public/logo192.png b/tools/clarodocs/public/logo192.png
deleted file mode 100644
index fc44b0a3..00000000
Binary files a/tools/clarodocs/public/logo192.png and /dev/null differ
diff --git a/tools/clarodocs/public/logo512.png b/tools/clarodocs/public/logo512.png
deleted file mode 100644
index a4e47a65..00000000
Binary files a/tools/clarodocs/public/logo512.png and /dev/null differ
diff --git a/tools/clarodocs/public/manifest.json b/tools/clarodocs/public/manifest.json
index 080d6c77..fe1c1a99 100644
--- a/tools/clarodocs/public/manifest.json
+++ b/tools/clarodocs/public/manifest.json
@@ -1,25 +1,25 @@
{
- "short_name": "React App",
- "name": "Create React App Sample",
+ "short_name": "ClaroDocs",
+ "name": "ClaroDocs",
"icons": [
{
- "src": "favicon.ico",
+ "src": "claro-logo.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
- "src": "logo192.png",
+ "src": "claro-logo-192x192.png",
"type": "image/png",
"sizes": "192x192"
},
{
- "src": "logo512.png",
+ "src": "claro-logo-512x512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
- "theme_color": "#000000",
+ "theme_color":"#ffffff",
"background_color": "#ffffff"
}
diff --git a/tools/clarodocs/src/App.tsx b/tools/clarodocs/src/App.tsx
index 65336909..020919d2 100644
--- a/tools/clarodocs/src/App.tsx
+++ b/tools/clarodocs/src/App.tsx
@@ -7,24 +7,53 @@ import { HighlightJS } from './components/highlight_js/HighlightJS';
import { DepGraph } from './components/dep_graph/DepGraph';
import { WholeProgramDepGraph } from './components/dep_graph/WholeProgramDepGraph';
import { ModuleTree } from './components/module_tree/ModuleTree';
+import {
+ createBrowserRouter,
+ RouterProvider,
+ useSearchParams
+} from "react-router-dom";
+// import { useSearchParamsState } from './hooks/UseSearchParamsHook';
import { Breadcrumb, ConfigProvider, Layout, Menu, theme } from 'antd';
const { Header, Sider, Content } = Layout;
-function App() {
- const [showDepGraph, setShowDepGraph] = useState(true);
- const {
- token: { colorBgContainer, borderRadiusLG },
- } = theme.useToken();
-
- const [ selectedModule, setSelectedModule ] = useState('');
- const [ targetType, setTargetType ] = useState('');
+function AppImpl() {
+ const [ searchParams, setSearchParams ] = useSearchParams();
- const [ ClaroModules, rootName, rootDeps ] = getClaroModules(setSelectedModule);
+ const showDepGraph = searchParams.get('showDepGraph') === null || searchParams.get('showDepGraph') === 'true';
+ let selectedModule = searchParams.get('selectedModule') || '';
+ const targetType = searchParams.get('targetType') || '';
- // We'll reuse these tooltip components every time the dep is referenced in the Module API.
- const selectedModuleDepsTooltips = ClaroModules[selectedModule]?.deps;
+ const setShowDepGraph = function() {
+ setSearchParams({
+ showDepGraph: !showDepGraph,
+ selectedModule: '',
+ targetType: ''
+ });
+ }
+ const setSelectedModule = function(newSelectedModule) {
+ setSearchParams({
+ showDepGraph: false,
+ selectedModule: newSelectedModule,
+ targetType: targetType
+ });
+ }
+ const setSelectedModuleAndTargetType = function(newSelectedModule, newTargetType) {
+ setSearchParams({
+ showDepGraph: false,
+ selectedModule: newSelectedModule,
+ targetType: newTargetType
+ });
+ }
+ const setTargetType = function(newTargetType) {
+ setSearchParams({
+ showDepGraph: showDepGraph,
+ selectedModule: selectedModule,
+ targetType: newTargetType
+ });
+ }
+ const [ ClaroModules, rootName, rootDeps ] = getClaroModules(setSelectedModule);
useEffect(
() => {
@@ -42,6 +71,19 @@ function App() {
);
function APIs() {
+ if (!selectedModule) {
+ // Ensure that this still works even if the user just clicked the APIs tab directly without manually selecting a
+ // module.
+ selectedModule = Object.keys(ClaroModules).toSorted()[0];
+ }
+
+ // We'll reuse these tooltip components every time the dep is referenced in the Module API.
+ const selectedModuleDepsTooltips = ClaroModules[selectedModule]?.deps;
+
+ const {
+ token: { colorBgContainer, borderRadiusLG },
+ } = theme.useToken();
+
return (
@@ -131,9 +173,8 @@ function App() {
className="claro"
style={{ textAlign: "left" }}
id={selectedModule}
- setSelectedModule={setSelectedModule}
targetType={targetType}
- setTargetType={setTargetType}
+ setSelectedMOduleAndTargetType={setSelectedModuleAndTargetType}
selectedModuleDepsTooltips={selectedModuleDepsTooltips}
>
{ClaroModules[selectedModule]?.api}
@@ -152,7 +193,6 @@ function App() {
modules={ClaroModules}
selectedModule={selectedModule}
setSelectedModule={setSelectedModule}
- setShowDepGraph={setShowDepGraph}
/>
);
}
@@ -170,14 +210,14 @@ function App() {
return (
-
+
@@ -189,4 +229,17 @@ function App() {
);
}
+function App() {
+ // We'll wrap everything in a React Router literally just so that it can easily maintain state from query params.
+ // The useSearchParams() function simply doesn't work outside of this RouterProvider context. Lame.
+ const router = createBrowserRouter([
+ {
+ path: "/",
+ element: ,
+ },
+ ]);
+
+ return
+}
+
export default App;
\ No newline at end of file
diff --git a/tools/clarodocs/src/BUILD b/tools/clarodocs/src/BUILD
index a2855d4a..5fe0171c 100644
--- a/tools/clarodocs/src/BUILD
+++ b/tools/clarodocs/src/BUILD
@@ -33,6 +33,7 @@ ts_project(
"//tools/clarodocs/src/components/input",
"//tools/clarodocs/src/components/module_tree",
"//tools/clarodocs/src/claro_module_apis",
+ "//tools/clarodocs/src/hooks:use_search_params_hook",
],
)
diff --git a/tools/clarodocs/src/claro_module_apis/demo.tsx b/tools/clarodocs/src/claro_module_apis/demo.tsx
index 76e0221c..98f89c10 100644
--- a/tools/clarodocs/src/claro_module_apis/demo.tsx
+++ b/tools/clarodocs/src/claro_module_apis/demo.tsx
@@ -17,7 +17,7 @@ export function getClaroModules(setSelectedModule) {
// Copy the data so that it's not overwritten.
const fmtMod = formatUniqueModuleName(mod);
claroModules[fmtMod] = {};
- claroModules[fmtMod]['api'] = data.depGraph[mod].api;
+ claroModules[fmtMod]['api'] = data.depGraph[mod].api.trim();
claroModules[fmtMod]['deps'] = {};
for (let dep of Object.keys(data.depGraph[mod].deps)) {
diff --git a/tools/clarodocs/src/claro_module_apis/testjson.json b/tools/clarodocs/src/claro_module_apis/testjson.json
deleted file mode 100644
index 8d45af30..00000000
--- a/tools/clarodocs/src/claro_module_apis/testjson.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
- "stdlib$http$http": {
- "api": "\nfunction getOk200HttpResponseForHtml(json: string) -> HttpResponse;\n\nfunction getOk200HttpResponseForJson(json: string) -> HttpResponse;\n",
- "deps": {}
- },
- "examples$claro_programs$demo_server$buggy_buggies$buggy_buggies_service$buggy_buggies_client": {
- "api": "\n# This encodes the public API of the Buggy-Buggies site available at https://buggy-buggies.gigalixirapp.com/.\n# Claro will generate a non-blocking RPC client for you via the following:\n# `var myClient: HttpClient = getHttpClient(\"https://buggy-buggies.gigalixirapp.com\");`\nHttpService BuggyBuggies {\n hostGame: \"/api/host/{handle}\",\n friendsJoin: \"/api/game/{gameId}/join/{handle}\",\n move: \"/api/game/{gameId}/player/{secret}/move/{direction}\",\n worldInfo: \"/api/game/{gameId}/player/{secret}/info\",\n reset: \"/api/game/{gameId}/player/{secret}/reset\"\n}\n\n# Now there's a single static definition of which client will be used for sending reqs to the Buggy Buggies server.\nstatic HTTP_CLIENT: HttpClient;\n\n# This type models the JSON response from the Buggy-Buggies service as a Claro type to enable parsing the response into\n# something that you can work with programmatically with strict type validation on the edge.\nalias MoveResponse : struct {\n reason: oneof,\n result: struct {\n players: { string : struct { x: int, y: int } },\n dimensions: struct {\n height: int,\n width: int\n },\n world: { string: string }, # E.g. (\"0,6\": \"wall\")\n you: struct {\n handle: string,\n purse: int,\n boom: boolean,\n x: int,\n y: int\n }\n },\n success: boolean\n}\n\nfunction getParsedMoveResponse(buggyResponse: string) -> std::ParsedJson;\n",
- "deps": {}
- },
- "examples$claro_programs$demo_server$buggy_buggies$data_structures$default_dict": {
- "api": "newtype DefaultDict: struct {defaultPr: provider, dict: mut {K:V}}\n\ninitializers DefaultDict {\n function create(defaultPr: provider) -> DefaultDict;\n}\n\n# TODO(steving) Claro really needs to support overloading operator[] via some Contract.\nunwrappers DefaultDict {\n function get(d: DefaultDict, key: K) -> V;\n consumer put(d: DefaultDict, key: K, value: V);\n function asMap(d: DefaultDict) -> {K: V};\n}\n",
- "deps": {}
- },
- "examples$claro_programs$demo_server$buggy_buggies$data_structures$position": {
- "api": "\nnewtype Position : struct {x: int, y: int}\n",
- "deps": {}
- },
- "examples$claro_programs$demo_server$buggy_buggies$data_structures$heap": {
- "api": "\n# This implementation is the result of asking Chat GPT to implement it in python for me and reworking it to fit in Claro\n# I'm not thinking too hard about this...\nnewtype Heap: mut [struct {dist: int, pos: Pos::Position}]\n\ninitializers Heap {\n provider getHeap() -> Heap;\n}\n\nunwrappers Heap {\n function extract_min(heap: Heap) -> oneof>;\n consumer insert(heap: Heap, value: struct {dist: int, pos: Pos::Position});\n function heapIsEmpty(heap: Heap) -> boolean;\n}\n",
- "deps": {"Pos": "examples$claro_programs$demo_server$buggy_buggies$data_structures$position"}
- },
- "examples$claro_programs$demo_server$buggy_buggies$buggy_agent$buggy_agent": {
- "api": "\nalias BestPath : [Pos::Position]\n\nfunction parseWorldMap(world: {string: string}) -> {Pos::Position: string};\nfunction dijkstra(world: {Pos::Position: string}, start: Pos::Position) -> BestPath;\nfunction movesFromBestPath(bestPath: BestPath) -> [string];\n",
- "deps": {"DefaultDict": "examples$claro_programs$demo_server$buggy_buggies$data_structures$default_dict",
- "Heaps": "examples$claro_programs$demo_server$buggy_buggies$data_structures$heap",
- "Pos": "examples$claro_programs$demo_server$buggy_buggies$data_structures$position"}
- },
- "examples$claro_programs$demo_server$buggy_buggies$utils$utils": {
- "api": "# This module is simply a dumping ground of uncategorized utility functions.\n\nfunction handleBuggyResponseAsHtmlStrParts(buggyResponse: oneof>) -> [string];\nfunction reduce(l: [T], accumulated: R, accumulatorFn: function<|R, T| -> R>) -> R;\n",
- "deps": {}
- },
- "examples$claro_programs$demo_server$buggy_buggies$endpoint_handlers$resources$resources": {
- "api": "\nstatic gamePageHtml : string;\n",
- "deps": {}
- },
- "examples$claro_programs$demo_server$buggy_buggies$endpoint_handlers$endpoint_handlers": {
- "api": "\n# It's worth noting that in the Claro way of the world, this distinction of whether a Procedure is implemented as a\n# Graph or not is considered completely an internal implementation consideration that's not directly observable with\n# in-language semantics. Hence, this is *actually* implemented internally as a Graph Procedure, but that detail just\n# isn't exposed here.\nprovider gamePageHandler() -> future;\n\nfunction startNewGameHandler(handle: string) -> future;\n\nfunction getBestMovesHandler(buggyResponse: oneof>) -> string;\n\nfunction gameMoveHandler(gameId: string, playerSecret: string, dir: string) -> future;\n",
- "deps": {"Agent": "examples$claro_programs$demo_server$buggy_buggies$buggy_agent$buggy_agent",
- "BuggyBuggies": "examples$claro_programs$demo_server$buggy_buggies$buggy_buggies_service$buggy_buggies_client",
- "Utils": "examples$claro_programs$demo_server$buggy_buggies$utils$utils",
- "Pos": "examples$claro_programs$demo_server$buggy_buggies$data_structures$position",
- "Resources": "examples$claro_programs$demo_server$buggy_buggies$endpoint_handlers$resources$resources"}
- }
-}
diff --git a/tools/clarodocs/src/components/dep_graph/WholeProgramDepGraph.tsx b/tools/clarodocs/src/components/dep_graph/WholeProgramDepGraph.tsx
index 2c99f538..c755635d 100644
--- a/tools/clarodocs/src/components/dep_graph/WholeProgramDepGraph.tsx
+++ b/tools/clarodocs/src/components/dep_graph/WholeProgramDepGraph.tsx
@@ -1,6 +1,6 @@
import { Mermaid } from '../mermaid/Mermaid';
-export function WholeProgramDepGraph({ rootName, rootDeps, modules, selectedModule, setSelectedModule, setShowDepGraph }) {
+export function WholeProgramDepGraph({ rootName, rootDeps, modules, selectedModule, setSelectedModule}) {
let mermaidGraph = 'graph LR;\n';
for (let rootDep of Object.keys(rootDeps).toSorted()) {
@@ -24,8 +24,7 @@ export function WholeProgramDepGraph({ rootName, rootDeps, modules, selectedModu
}
// Every node navigates to its Module API page.
nodeOnClickCallbacks[module[0]] = () => {
- setSelectedModule(module[0]);
- setShowDepGraph(false); // Navigate to the API.
+ setSelectedModule(module[0]); // Navigate to the API.
}
}
diff --git a/tools/clarodocs/src/components/highlight_js/HighlightJS.tsx b/tools/clarodocs/src/components/highlight_js/HighlightJS.tsx
index 95b3f0d9..68f050ed 100644
--- a/tools/clarodocs/src/components/highlight_js/HighlightJS.tsx
+++ b/tools/clarodocs/src/components/highlight_js/HighlightJS.tsx
@@ -47,8 +47,7 @@ export class HighlightJS extends React.Component {
const nextNode = codeEl.childNodes[i + 1];
if (this.isExpectedSpan(nextNode, '::')) {
// Make sure that `this` doesn't escape the current scope.
- const setSelectedModule = this.props.setSelectedModule;
- const setTargetType = this.props.setTargetType;
+ const setSelectedMOduleAndTargetType = this.props.setSelectedMOduleAndTargetType;
const depName = el.textContent.trim();
const targetType = codeEl.childNodes[i + 2].textContent.trim();
@@ -62,8 +61,7 @@ export class HighlightJS extends React.Component {
const _selectedModule = this.props.selectedModuleDepsTooltips[depName].path;
function _setSelectedModule(e) {
- setSelectedModule(_selectedModule);
- setTargetType(targetType);
+ setSelectedMOduleAndTargetType(_selectedModule, targetType);
}
// First replace the dep reference with its tooltip
const newTargetTypeNode = document.createElement('span');
diff --git a/tools/clarodocs/src/hooks/BUILD b/tools/clarodocs/src/hooks/BUILD
new file mode 100644
index 00000000..d11553de
--- /dev/null
+++ b/tools/clarodocs/src/hooks/BUILD
@@ -0,0 +1,15 @@
+load("@aspect_rules_ts//ts:defs.bzl", "ts_project")
+load("//tools/clarodocs:defs.bzl", "TRANSPILER")
+
+ts_project(
+ name = "use_search_params_hook",
+ srcs = ["UseSearchParamsHook.tsx"],
+ declaration = True,
+ resolve_json_module = True,
+ transpiler = TRANSPILER,
+ tsconfig = "//tools/clarodocs:tsconfig",
+ visibility = ["//tools/clarodocs:__subpackages__"],
+ deps = [
+ "//tools/clarodocs:node_modules/react-router-dom",
+ ],
+)
diff --git a/tools/clarodocs/src/hooks/UseSearchParamsHook.tsx b/tools/clarodocs/src/hooks/UseSearchParamsHook.tsx
new file mode 100644
index 00000000..3147f572
--- /dev/null
+++ b/tools/clarodocs/src/hooks/UseSearchParamsHook.tsx
@@ -0,0 +1,28 @@
+// Documented at https://blog.logrocket.com/use-state-url-persist-state-usesearchparams/.
+import { useSearchParams } from "react-router-dom";
+
+export function useSearchParamsState(
+ searchParamName: string,
+ defaultValue: string
+): readonly [
+ searchParamsState: string,
+ setSearchParamsState: (newState: string) => void
+] {
+ const [searchParams, setSearchParams] = useSearchParams();
+
+ const acquiredSearchParam = searchParams.get(searchParamName);
+ const searchParamsState = acquiredSearchParam ?? defaultValue;
+
+ const setSearchParamsState = (newState: string) => {
+ const next = Object.assign(
+ {},
+ [...searchParams.entries()].reduce(
+ (o, [key, value]) => ({ ...o, [key]: value }),
+ {}
+ ),
+ { [searchParamName]: newState }
+ );
+ setSearchParams(next);
+ };
+ return [searchParamsState, setSearchParamsState];
+}
\ No newline at end of file