diff --git a/package.json b/package.json index a236846..6852859 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,7 @@ "@dimforge/rapier2d": "latest", "@tldraw/tldraw": "2.0.2", "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-helmet-async": "^2.0.4" + "react-dom": "^18.2.0" }, "devDependencies": { "@biomejs/biome": "1.4.1", @@ -25,7 +24,6 @@ "@types/react-dom": "^18.2.7", "@vitejs/plugin-react": "^4.0.3", "concurrently": "^8.2.0", - "gh-pages": "^6.1.1", "typescript": "^5.0.2", "vite": "^4.4.5", "vite-plugin-top-level-await": "^1.3.1", diff --git a/src/App.tsx b/src/App.tsx index 5fd3a02..f7d0bae 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,35 +1,16 @@ -import { createShapeId, Tldraw, TLGeoShape, TLShape, TLUiComponents } from "@tldraw/tldraw"; +import { createShapeId, TLUiComponents } from "@tldraw/tldraw"; import "@tldraw/tldraw/tldraw.css"; import "./css/style.css" -import { SimControls } from "./physics/ui/PhysicsControls"; -import { uiOverrides } from "./physics/ui/overrides"; -import { Helmet, HelmetProvider } from "react-helmet-async"; import React, { useEffect, useState } from "react"; import ReactDOM from "react-dom/client"; -import { HTMLShapeUtil, HTMLShape } from "./HTMLShapeUtil"; +import { HTMLShape } from "./ts/shapes/HTMLShapeUtil"; +import { Default } from "./ts/components/Default"; +import { Canvas } from "./ts/components/Canvas"; +import { Toggle } from "./ts/components/Toggle"; +import { gatherElementsInfo } from "./utils"; ReactDOM.createRoot(document.getElementById("root")!).render(); -const components: TLUiComponents = { - HelpMenu: null, - StylePanel: null, - PageMenu: null, - NavigationPanel: null, - DebugMenu: null, - ContextMenu: null, - ActionsMenu: null, - QuickActions: null, - MainMenu: null, - MenuPanel: null, - // ZoomMenu: null, - // Minimap: null, - // Toolbar: null, - // KeyboardShortcutsDialog: null, - // HelperButtons: null, - // SharePanel: null, - // TopPanel: null, -} - function App() { const [isPhysicsEnabled, setIsPhysicsEnabled] = useState(false); const [elementsInfo, setElementsInfo] = useState([]); @@ -57,31 +38,7 @@ function App() { }; }, [isPhysicsEnabled]); - // Function to gather elements info asynchronously - async function gatherElementsInfo() { - const rootElement = document.getElementsByTagName('main')[0]; - const info: any[] = []; - if (rootElement) { - for (const child of rootElement.children) { - if (['BUTTON'].includes(child.tagName)) continue - const rect = child.getBoundingClientRect(); - let w = rect.width - // if (!['P', 'UL'].includes(child.tagName)) { - // w = measureElementTextWidth(child); - // } - // console.log(w) - info.push({ - tagName: child.tagName, - x: rect.left, - y: rect.top, - w: w, - h: rect.height, - html: child.outerHTML - }); - }; - } - return info; - } + const shapes: HTMLShape[] = elementsInfo.map((element) => ({ id: createShapeId(), @@ -97,157 +54,29 @@ function App() { shapes.push({ id: createShapeId(), - type: 'html', + type: 'geo', x: 0, y: window.innerHeight, props: { w: window.innerWidth, - h: 20, - html: "FOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" + h: 50, + color: 'grey', + fill: 'solid' + }, + meta: { + fixed: true } }) return ( - - -
- {} -
- {isPhysicsEnabled && elementsInfo.length > 0 ? : null} -
+ +
+ {} +
+ {isPhysicsEnabled && elementsInfo.length > 0 ? : null}
); }; -function Default() { - return ( -
- {/* - - */} -
- Orion Reed -
-

Hello! 👋

-

- My research investigates the intersection of computing, human-system - interfaces, and emancipatory politics. I am interested in the - potential of computing as a medium for thought, as a tool for - collective action, and as a means of emancipation. -

-

- My current focus is basic research into the nature of digital - organisation, developing theoretical toolkits to improve shared - infrastructure, and applying this research to the design of new - systems and protocols which support the self-organisation of knowledge - and computational artifacts. -

- -

My work

-

- Alongside my independent work I am a researcher at Block Science building - knowledge organisation infrastructure and at ECSA working on - computational media. I am also part of the nascent Liberatory Computing - collective and a co-organiser of the OCWG. -

- -

Get in touch

-

- I am on Twitter as @OrionReedOne and on - Mastodon as @orion@hci.social. The best way to reach me is - through Twitter or my email, me@orionreed.com -

- - *** - -

Talks

- -

Writing

- -
- ); -} - -function Canvas({ shapes }: { shapes: TLShape[] }) { - - return ( -
- {/* - - */} - - - -
- ); -} - -function Toggle() { - return ( - <> - {/* - - */} - - - ); -} - -function Contact() { - return ( -
-

Contact

-

Twitter: @OrionReedOne

-

Mastodon: orion@hci.social

-

Email: me@orionreed.com

-

GitHub: OrionReed

-
- - ) -} - -function measureElementTextWidth(element: Element) { - // Create a temporary span element - const tempElement = document.createElement('span'); - // Get the text content from the passed element - tempElement.textContent = element.textContent || element.innerText; - // Get the computed style of the passed element - const computedStyle = window.getComputedStyle(element); - // Apply relevant styles to the temporary element - tempElement.style.font = computedStyle.font; - tempElement.style.fontWeight = computedStyle.fontWeight; - tempElement.style.fontSize = computedStyle.fontSize; - tempElement.style.fontFamily = computedStyle.fontFamily; - tempElement.style.letterSpacing = computedStyle.letterSpacing; - // Ensure the temporary element is not visible in the viewport - tempElement.style.position = 'absolute'; - tempElement.style.visibility = 'hidden'; - tempElement.style.whiteSpace = 'nowrap'; // Prevent text from wrapping - // Append to the body to make measurements possible - document.body.appendChild(tempElement); - // Measure the width - const width = tempElement.getBoundingClientRect().width; - // Remove the temporary element from the document - document.body.removeChild(tempElement); - // Return the measured width - return width === 0 ? 10 : width; -} \ No newline at end of file diff --git a/src/objects/causal-islands-integration-domain.pdf b/src/artifacts/causal-islands-integration-domain.pdf similarity index 100% rename from src/objects/causal-islands-integration-domain.pdf rename to src/artifacts/causal-islands-integration-domain.pdf diff --git a/src/css/style.css b/src/css/style.css index 57a84cb..8bf3b75 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -134,7 +134,7 @@ ul { .fade-out { opacity: 0 !important; - transition: opacity 1s ease-in-out; + transition: opacity 0.2s ease-in-out; /* visibility: hidden; */ /* display: none; */ } diff --git a/src/physics/ui/overrides.ts b/src/physics/ui/overrides.ts deleted file mode 100644 index ae905e7..0000000 --- a/src/physics/ui/overrides.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - TLUiEventSource, - TLUiOverrides, - TLUiTranslationKey, -} from "@tldraw/tldraw"; - -// In order to see select our custom shape tool, we need to add it to the ui. -export const uiOverrides: TLUiOverrides = { - actions(_editor, actions) { - actions['toggle-physics'] = { - id: 'toggle-physics', - label: 'Toggle Physics' as TLUiTranslationKey, - readonlyOk: true, - kbd: 'p', - onSelect(_source: TLUiEventSource) { - const event = new CustomEvent('togglePhysicsEvent'); - window.dispatchEvent(event); - }, - } - return actions - }, -} \ No newline at end of file diff --git a/src/ts/components/Canvas.tsx b/src/ts/components/Canvas.tsx new file mode 100644 index 0000000..e4c5c80 --- /dev/null +++ b/src/ts/components/Canvas.tsx @@ -0,0 +1,37 @@ +import { Tldraw, TLShape, TLUiComponents } from "@tldraw/tldraw"; +import { SimController } from "../physics/PhysicsControls"; +import { HTMLShapeUtil } from "../shapes/HTMLShapeUtil"; + +const components: TLUiComponents = { + HelpMenu: null, + StylePanel: null, + PageMenu: null, + NavigationPanel: null, + DebugMenu: null, + ContextMenu: null, + ActionsMenu: null, + QuickActions: null, + MainMenu: null, + MenuPanel: null, + // ZoomMenu: null, + // Minimap: null, + // Toolbar: null, + // KeyboardShortcutsDialog: null, + // HelperButtons: null, + // SharePanel: null, + // TopPanel: null, +} + +export function Canvas({ shapes }: { shapes: TLShape[]; }) { + + return ( +
+ + + +
+ ); +} diff --git a/src/ts/components/Contact.tsx b/src/ts/components/Contact.tsx new file mode 100644 index 0000000..5838f93 --- /dev/null +++ b/src/ts/components/Contact.tsx @@ -0,0 +1,14 @@ +import React from "react"; + +function Contact() { + return ( +
+

Contact

+

Twitter: @OrionReedOne

+

Mastodon: orion@hci.social

+

Email: me@orionreed.com

+

GitHub: OrionReed

+
+ + ); +} diff --git a/src/ts/components/Default.tsx b/src/ts/components/Default.tsx new file mode 100644 index 0000000..f0f16c2 --- /dev/null +++ b/src/ts/components/Default.tsx @@ -0,0 +1,59 @@ +import React from "react"; + +export function Default() { + return ( +
+
+ Orion Reed +
+

Hello! 👋

+

+ My research investigates the intersection of computing, human-system + interfaces, and emancipatory politics. I am interested in the + potential of computing as a medium for thought, as a tool for + collective action, and as a means of emancipation. +

+ +

+ My current focus is basic research into the nature of digital + organisation, developing theoretical toolkits to improve shared + infrastructure, and applying this research to the design of new + systems and protocols which support the self-organisation of knowledge + and computational artifacts. +

+ +

My work

+

+ Alongside my independent work I am a researcher at Block Science building + knowledge organisation infrastructure and at ECSA working on + computational media. I am also part of the nascent Liberatory Computing + collective and a co-organiser of the OCWG. +

+ +

Get in touch

+

+ I am on Twitter as @OrionReedOne and on + Mastodon as @orion@hci.social. The best way to reach me is + through Twitter or my email, me@orionreed.com +

+ + *** + +

Talks

+ +

Writing

+ +
+ ); +} diff --git a/src/ts/components/Toggle.tsx b/src/ts/components/Toggle.tsx new file mode 100644 index 0000000..d7614f8 --- /dev/null +++ b/src/ts/components/Toggle.tsx @@ -0,0 +1,11 @@ +import React from "react"; + +export function Toggle() { + return ( + <> + + + ); +} diff --git a/src/physics/ui/PhysicsControls.tsx b/src/ts/physics/PhysicsControls.tsx similarity index 72% rename from src/physics/ui/PhysicsControls.tsx rename to src/ts/physics/PhysicsControls.tsx index e15fbb5..444f4a0 100644 --- a/src/physics/ui/PhysicsControls.tsx +++ b/src/ts/physics/PhysicsControls.tsx @@ -1,8 +1,8 @@ import { TLUnknownShape, useEditor } from "@tldraw/tldraw"; import { useEffect } from "react"; -import { usePhysicsSimulation } from "../simulation"; +import { usePhysicsSimulation } from "./simulation"; -export const SimControls = ({ shapes }: { shapes: TLUnknownShape[] }) => { +export const SimController = ({ shapes }: { shapes: TLUnknownShape[] }) => { const editor = useEditor(); useEffect(() => { diff --git a/src/physics/config.ts b/src/ts/physics/config.ts similarity index 100% rename from src/physics/config.ts rename to src/ts/physics/config.ts diff --git a/src/physics/math.ts b/src/ts/physics/math.ts similarity index 100% rename from src/physics/math.ts rename to src/ts/physics/math.ts diff --git a/src/physics/simulation.ts b/src/ts/physics/simulation.ts similarity index 97% rename from src/physics/simulation.ts rename to src/ts/physics/simulation.ts index 3829d2d..f73900f 100644 --- a/src/physics/simulation.ts +++ b/src/ts/physics/simulation.ts @@ -9,6 +9,8 @@ type BodyWithShapeData = RAPIER.RigidBody & { }; type RigidbodyLookup = { [key: TLShapeId]: RAPIER.RigidBody }; +const START_DELAY = 1500; + export class PhysicsWorld { private editor: Editor; private world: RAPIER.World; @@ -28,6 +30,7 @@ export class PhysicsWorld { public start() { this.world = new RAPIER.World(GRAVITY); + // setTimeout(() => { this.addShapes(this.editor.getCurrentPageShapes()); const simLoop = () => { @@ -38,6 +41,7 @@ export class PhysicsWorld { }; simLoop(); return () => cancelAnimationFrame(this.animFrame); + // }, START_DELAY); }; public stop() { @@ -55,6 +59,7 @@ export class PhysicsWorld { } switch (shape.type) { + case "html": case "geo": case "image": case "video": @@ -72,12 +77,12 @@ export class PhysicsWorld { } createShape(shape: TLGeoShape | TLDrawShape) { - if (shape.props.dash === "dashed") return; // Skip dashed shapes - if (isRigidbody(shape.props.color)) { - const gravity = getGravityFromColor(shape.props.color) - const rb = this.createRigidbody(shape, gravity); + console.log('creating shape'); + if (!shape.meta.fixed) { + const rb = this.createRigidbody(shape, 1); this.createCollider(shape, rb); - } else { + } + else { this.createCollider(shape); } } @@ -379,7 +384,6 @@ export function usePhysicsSimulation(editor: Editor, enabled: boolean) { useEffect(() => { if (enabled) { sim.current.start(); - editor.selectNone(); return () => sim.current.stop(); } }, [enabled, sim]); diff --git a/src/HTMLShapeUtil.tsx b/src/ts/shapes/HTMLShapeUtil.tsx similarity index 95% rename from src/HTMLShapeUtil.tsx rename to src/ts/shapes/HTMLShapeUtil.tsx index 7503eb3..156b261 100644 --- a/src/HTMLShapeUtil.tsx +++ b/src/ts/shapes/HTMLShapeUtil.tsx @@ -23,7 +23,7 @@ export class HTMLShapeUtil extends ShapeUtil { } component(shape: HTMLShape) { - return
+ return
} indicator(shape: HTMLShape) { diff --git a/src/utils.tsx b/src/utils.tsx new file mode 100644 index 0000000..4ab56b6 --- /dev/null +++ b/src/utils.tsx @@ -0,0 +1,61 @@ +export function measureElementTextWidth(element: HTMLElement) { + // Create a temporary span element + const tempElement = document.createElement('span'); + // Get the text content from the passed element + tempElement.textContent = element.textContent || element.innerText; + // Get the computed style of the passed element + const computedStyle = window.getComputedStyle(element); + // Apply relevant styles to the temporary element + tempElement.style.font = computedStyle.font; + tempElement.style.fontWeight = computedStyle.fontWeight; + tempElement.style.fontSize = computedStyle.fontSize; + tempElement.style.fontFamily = computedStyle.fontFamily; + tempElement.style.letterSpacing = computedStyle.letterSpacing; + // Ensure the temporary element is not visible in the viewport + tempElement.style.position = 'absolute'; + tempElement.style.visibility = 'hidden'; + tempElement.style.whiteSpace = 'nowrap'; // Prevent text from wrapping + // Append to the body to make measurements possible + document.body.appendChild(tempElement); + // Measure the width + const width = tempElement.getBoundingClientRect().width; + // Remove the temporary element from the document + document.body.removeChild(tempElement); + // Return the measured width + return width === 0 ? 10 : width; +} + + +// Function to gather elements info asynchronously +export async function gatherElementsInfo() { + const rootElement = document.getElementsByTagName('main')[0]; + const info: any[] = []; + if (rootElement) { + for (const child of rootElement.children) { + if (['BUTTON'].includes(child.tagName)) continue; + const rect = child.getBoundingClientRect(); + let w = rect.width; + if (!['P', 'UL'].includes(child.tagName)) { + w = measureElementTextWidth(child); + } + // Check if the element is centered + const computedStyle = window.getComputedStyle(child); + let x = rect.left; // Default x position + if (computedStyle.display === 'block' && computedStyle.textAlign === 'center') { + // Adjust x position for centered elements + const parentWidth = child.parentElement ? child.parentElement.getBoundingClientRect().width : 0; + x = (parentWidth - w) / 2 + window.scrollX + (child.parentElement ? child.parentElement.getBoundingClientRect().left : 0); + } + + info.push({ + tagName: child.tagName, + x: x, + y: rect.top, + w: w, + h: rect.height, + html: child.outerHTML + }); + }; + } + return info; +}