{"id":20940212,"url":"https://github.com/simbroadcasts/react-node-insim","last_synced_at":"2025-05-13T22:32:16.120Z","repository":{"id":209741246,"uuid":"626974452","full_name":"simbroadcasts/react-node-insim","owner":"simbroadcasts","description":"React renderer for in-game buttons for Live for Speed","archived":false,"fork":false,"pushed_at":"2025-02-22T23:15:02.000Z","size":5044,"stargazers_count":6,"open_issues_count":5,"forks_count":0,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-05-05T22:02:38.722Z","etag":null,"topics":["api","buttons","insim","lfs","live-for-speed","node","node-insim","node-js","nodejs","racing","react","react-reconciler","react-renderer","renderer","simulator"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simbroadcasts.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-12T14:28:12.000Z","updated_at":"2025-04-09T13:32:30.000Z","dependencies_parsed_at":"2023-12-24T19:48:29.186Z","dependency_job_id":"d3197546-729c-4680-be5f-b567e2e1dd9f","html_url":"https://github.com/simbroadcasts/react-node-insim","commit_stats":{"total_commits":107,"total_committers":2,"mean_commits":53.5,"dds":0.009345794392523366,"last_synced_commit":"451dbe315d69f6b5ec92faa7a96a17fd0870b818"},"previous_names":["simbroadcasts/react-node-insim"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simbroadcasts%2Freact-node-insim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simbroadcasts%2Freact-node-insim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simbroadcasts%2Freact-node-insim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simbroadcasts%2Freact-node-insim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simbroadcasts","download_url":"https://codeload.github.com/simbroadcasts/react-node-insim/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254036821,"owners_count":22003659,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","buttons","insim","lfs","live-for-speed","node","node-insim","node-js","nodejs","racing","react","react-reconciler","react-renderer","renderer","simulator"],"created_at":"2024-11-18T23:09:29.550Z","updated_at":"2025-05-13T22:32:16.107Z","avatar_url":"https://github.com/simbroadcasts.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React Node InSim\n\n\u003cimg src=\"https://simbroadcasts.tv/assets/node-insim/react-node-insim-icon-256.png\" width=\"100px\" align=\"right\" hspace=\"15\"\u003e\n\n[![NPM Version](https://img.shields.io/npm/v/react-node-insim?style=flat-square)](https://www.npmjs.com/package/react-node-insim) \u003c!--![Node.js CI](https://github.com/simbroadcasts/react-node-insim/actions/workflows/.github/workflows/validate.yml/badge.svg)--\u003e\n\nA [React renderer](https://legacy.reactjs.org/docs/codebase-overview.html#renderers) for [InSim](https://en.lfsmanual.net/wiki/InSim.txt) buttons, based on [Node InSim](https://github.com/simbroadcasts/node-insim).\n\n## Introduction\n\n\u003e 🚧 This project is still under development. Any API may change as needed.\n\nReact Node InSim is a [React renderer](https://legacy.reactjs.org/docs/codebase-overview.html#renderers) for [Live for Speed](https://www.lfs.net/) [InSim](https://en.lfsmanual.net/wiki/InSim.txt) buttons. It also provides [layout components](#horizontal-stack) for easier button positioning, [hooks](#hooks) for handling incoming InSim packets and tracking server connections \u0026 players.\n\nIt is based on [Node InSim](https://github.com/simbroadcasts/node-insim), a Node.js library, written in TypeScript, for InSim communication.\n\nIt allows you to create things like this:\n\n\u003cimg src=\"docs/insim-buttons-preview.gif\" width=\"400\" alt=\"Live list of connections and players\" /\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eShow source code\u003c/summary\u003e\n\n```tsx\nimport type { InSim } from 'node-insim';\nimport { InSimFlags, IS_BTC, IS_MST, PacketType } from 'node-insim/packets';\nimport { StrictMode } from 'react';\nimport {\n  Button,\n  ConnectionsPlayersProvider,\n  createRoot,\n  useConnections,\n  useOnConnect,\n  useOnPacket,\n  usePlayers,\n  VStack,\n} from 'react-node-insim';\n\nfunction App() {\n  // Get the list of current players and connections\n  const players = usePlayers();\n  const connections = useConnections();\n\n  // Do something after the InSim app has been connected to LFS\n  useOnConnect((packet, inSim) =\u003e {\n    console.log(`Connected to LFS ${packet.Product} ${packet.Version}`);\n    inSim.send(new IS_MST({ Msg: `React Node InSim connected` }));\n  });\n\n  // Handle incoming packets\n  useOnPacket(PacketType.ISP_NCN, (packet) =\u003e {\n    console.log(`New connection: ${packet.UName}`);\n  });\n\n  // Clickable buttons\n  const handlePlayerClick = (plid: number) =\u003e (_: IS_BTC, inSim: InSim) =\u003e {\n    inSim.send(new IS_MST({ Msg: `/echo PLID ${plid}` }));\n  };\n\n  const handleConnectionClick = (ucid: number) =\u003e (_: IS_BTC, inSim: InSim) =\u003e {\n    inSim.send(new IS_MST({ Msg: `/echo UCID ${ucid}` }));\n  };\n\n  return (\n    \u003c\u003e\n      \u003cButton top={10} left={40} width={30} height={5} UCID={255} color=\"title\"\u003e\n        Players\n      \u003c/Button\u003e\n      \u003cVStack\n        background=\"dark\"\n        top={15}\n        left={40}\n        width={30}\n        height={5}\n        UCID={255}\n      \u003e\n        {players.map((player) =\u003e (\n          \u003cButton key={player.PLID} onClick={handlePlayerClick(player.PLID)}\u003e\n            {player.PName}\n          \u003c/Button\u003e\n        ))}\n      \u003c/VStack\u003e\n      \u003cButton top={10} left={70} width={30} height={5} UCID={255} color=\"title\"\u003e\n        Connections\n      \u003c/Button\u003e\n      \u003cVStack\n        background=\"dark\"\n        top={15}\n        left={70}\n        width={30}\n        height={5}\n        UCID={255}\n      \u003e\n        {connections.map((connection) =\u003e (\n          \u003cButton\n            key={connection.UCID}\n            onClick={handleConnectionClick(connection.UCID)}\n          \u003e\n            {connection.UName}\n          \u003c/Button\u003e\n        ))}\n      \u003c/VStack\u003e\n    \u003c/\u003e\n  );\n}\n\nconst root = createRoot({\n  name: 'React InSim',\n  host: '127.0.0.1',\n  port: 29999,\n  flags: InSimFlags.ISF_LOCAL,\n});\n\nroot.render(\n  \u003cStrictMode\u003e\n    \u003cConnectionsPlayersProvider\u003e\n      \u003cApp /\u003e\n    \u003c/ConnectionsPlayersProvider\u003e\n  \u003c/StrictMode\u003e,\n);\n```\n\n\u003c/details\u003e\n\n## Table of contents\n\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Basic usage](#basic-usage)\n- [Button](#button)\n  - [Placement](#placement)\n  - [Sizes](#sizes)\n  - [Variants](#variants)\n  - [Text colors](#text-colors)\n  - [Background colors](#background-colors)\n- [Horizontal stack](#horizontal-stack)\n- [Vertical stack](#vertical-stack)\n- [Flex](#flex)\n- [Grid](#grid)\n- [Toggle button](#toggle-button)\n  - [Variants](#variants-1)\n  - [Disabled state](#disabled-state)\n- [Toggle button group](#toggle-button-group)\n- [Text box](#text-box)\n- [Hooks](#hooks)\n  - [`useOnConnect`](#useonconnect)\n  - [`useOnDisconnect`](#useondisconnect)\n  - [`useOnPacket`](#useonpacket)\n  - [`useConnections`](#useconnections)\n  - [`usePlayers`](#useplayers)\n  - [`useRaceControlMessage`](#useracecontrolmessage)\n  - [`useInSim`](#useinsim)\n- [Scopes](#scopes)\n  - [Connection scope](#connection-scope)\n  - [Human player scope](#human-player-scope)\n  - [Global scope](#global-scope)\n- [Development](#development)\n\n## Requirements\n\n- [Node.js](https://nodejs.org/) 14 or higher\n- [Node InSim](https://github.com/simbroadcasts/node-insim)\n- [React](https://github.com/facebook/react) 18\n\n## Installation\n\n[NPM](https://www.npmjs.com/)\n\n```shell\nnpm install react@18 node-insim react-node-insim\n```\n\n[Yarn](https://classic.yarnpkg.com/en/docs)\n\n```shell\nyarn add react@18 node-insim react-node-insim\n```\n\n[pnpm](https://pnpm.io/)\n\n```shell\npnpm add react@18 node-insim react-node-insim\n```\n\n## Basic usage\n\nDisplaying an InSim button on a local computer\n\n```tsx\nimport { InSimFlags } from 'node-insim/packets';\nimport { Button, createRoot } from 'react-node-insim';\n\nconst root = createRoot({\n  name: 'React InSim',\n  host: '127.0.0.1',\n  port: 29999,\n  flags: InSimFlags.ISF_LOCAL,\n});\n\nroot.render(\n  \u003cButton top={100} left={80} width={30} height={10}\u003e\n    Hello InSim!\n  \u003c/Button\u003e,\n);\n```\n\nYou can use [React hooks](https://react.dev/reference/react) as usual to display stateful data via InSim.\n\n\u003cimg src=\"docs/button-current-time.gif\" width=\"300\" alt=\"Button showing current time\" /\u003e\n\n```tsx\nimport { InSimFlags } from 'node-insim/packets';\nimport { useEffect, useState } from 'react';\nimport { Button, createRoot } from 'react-node-insim';\n\nfunction App() {\n  const [time, setTime] = useState(new Date());\n\n  useEffect(() =\u003e {\n    const interval = setInterval(() =\u003e {\n      setTime(new Date());\n    }, 1000);\n\n    return () =\u003e {\n      clearInterval(interval);\n    };\n  });\n\n  return (\n    \u003cButton top={100} left={80} width={40} height={10}\u003e\n      Current time: {time.toLocaleTimeString()}\n    \u003c/Button\u003e\n  );\n}\n\nconst root = createRoot({\n  name: 'React InSim',\n  host: '127.0.0.1',\n  port: 29999,\n  flags: InSimFlags.ISF_LOCAL,\n});\n\nroot.render(\u003cApp /\u003e);\n```\n\n## Button\n\nThe Button component is used to display a button in LFS.\n\n- Buttons are drawn on a 200 by 200 canvas using absolute positioning\n- The maximum number of rendered buttons on a screen is 240\n\n### Import\n\n```ts\nimport { Button } from 'react-node-insim';\n```\n\n### Usage\n\n\u003cimg src=\"docs/button.png\" alt=\"Button\" /\u003e\n\n```tsx\n\u003cButton top={100} left={80} width={30} height={10}\u003e\n  Button\n\u003c/Button\u003e\n```\n\n#### Placement\n\nButtons use XY coordinates to position themselves on the screen. The `top` and `left` props control the button's X and Y position on the screen. The allowed range of values is 0 to 200.\n\n\u003cimg src=\"docs/button-placement.png\" alt=\"Button placement\" /\u003e\n\n```tsx\n\u003c\u003e\n  \u003cButton width={12} height={6} top={100} left={40}\u003e\n    Button\n  \u003c/Button\u003e\n  \u003cButton width={12} height={6} top={100} left={53}\u003e\n    Button\n  \u003c/Button\u003e\n  \u003cButton width={12} height={6} top={106} left={40}\u003e\n    Button\n  \u003c/Button\u003e\n  \u003cButton width={12} height={6} top={106} left={53}\u003e\n    Button\n  \u003c/Button\u003e\n\u003c/\u003e\n```\n\n#### Sizes\n\nUse the `width` and `height` props to change the dimensions of the button. The allowed range of values is 0 to 200.\n\n\u003cimg src=\"docs/button-sizes.png\" alt=\"Button sizes\" /\u003e\n\n```tsx\n\u003c\u003e\n  \u003cButton variant=\"light\" top={100} left={40} width={6} height={4}\u003e\n    Button\n  \u003c/Button\u003e\n  \u003cButton variant=\"light\" top={99} left={47} width={10} height={6}\u003e\n    Button\n  \u003c/Button\u003e\n  \u003cButton variant=\"light\" top={97} left={58} width={14} height={10}\u003e\n    Button\n  \u003c/Button\u003e\n\u003c/\u003e\n```\n\n#### Variants\n\nUse the `variant` prop to change the button's visual style. You can use `light` or `dark`. If you don't specify a variant, the button will have transparent background and a light gray text color.\n\n\u003cimg src=\"docs/button-variants.png\" alt=\"Button variants\" /\u003e\n\n```tsx\n\u003c\u003e\n  \u003cButton top={100} left={40} width={12} height={6} variant=\"light\"\u003e\n    Button\n  \u003c/Button\u003e\n  \u003cButton top={100} left={53} width={12} height={6} variant=\"dark\"\u003e\n    Button\n  \u003c/Button\u003e\n\u003c/\u003e\n```\n\n#### Text colors\n\nUse the `color` prop to customize the button's text color. If you don't specify a color, the button text will be `default`.\n\n\u003cimg src=\"docs/button-text-colors.png\" alt=\"Button text colors\" /\u003e\n\n```tsx\n\u003c\u003e\n  \u003cButton top={73} left={40} width={12} height={6} color=\"default\"\u003e\n    default\n  \u003c/Button\u003e\n  \u003cButton top={73} left={53} width={12} height={6} color=\"title\"\u003e\n    title\n  \u003c/Button\u003e\n  \u003cButton top={73} left={66} width={12} height={6} color=\"unselected\"\u003e\n    unselected\n  \u003c/Button\u003e\n  \u003cButton top={73} left={79} width={12} height={6} color=\"selected\"\u003e\n    selected\n  \u003c/Button\u003e\n  \u003cButton top={80} left={40} width={12} height={6} color=\"ok\"\u003e\n    ok\n  \u003c/Button\u003e\n  \u003cButton top={80} left={53} width={12} height={6} color=\"cancel\"\u003e\n    cancel\n  \u003c/Button\u003e\n  \u003cButton top={80} left={66} width={12} height={6} color=\"textstring\"\u003e\n    textstring\n  \u003c/Button\u003e\n  \u003cButton top={80} left={79} width={12} height={6} color=\"unavailable\"\u003e\n    unavailable\n  \u003c/Button\u003e\n\u003c/\u003e\n```\n\nYou can choose from a set of semantic colors or use one of the colors from the LFS color palette.\n\n**Semantic colors**\n\n- default\n- title\n- unselected\n- selected\n- ok\n- cancel\n- textstring\n- unavailable\n\n\u003e Note: The semantic color values can be customized in LFS Options -\u003e Display -\u003e Interface.\n\n**LFS color palette**\n\n- black\n- red\n- green\n- yellow\n- blue\n- magenta\n- cyan\n- white\n\n#### Background colors\n\nUse the `background` prop to customize the button's background color. If you don't specify a color, the background will be transparent.\n\n\u003cimg src=\"docs/button-background-colors.png\" alt=\"Button background colors\" /\u003e\n\n```tsx\n\u003c\u003e\n  \u003cButton top={67} left={40} width={12} height={6} background=\"light\"\u003e\n    light\n  \u003c/Button\u003e\n  \u003cButton top={67} left={53} width={12} height={6} background=\"dark\"\u003e\n    dark\n  \u003c/Button\u003e\n  \u003cButton top={67} left={66} width={12} height={6} background=\"transparent\"\u003e\n    transparent\n  \u003c/Button\u003e\n\u003c/\u003e\n```\n\n## Horizontal stack\n\n`HStack` displays buttons in a column without having to specify each button's position manually. You can also override button colors and sizes.\n\n### Import\n\n```ts\nimport { HStack } from 'react-node-insim';\n```\n\n### Usage\n\n\u003cimg src=\"docs/horizontal-stack.png\" alt=\"Horizontal stack\" /\u003e\n\n```tsx\n\u003cHStack top={10} left={20} width={7} height={4} variant=\"dark\"\u003e\n  \u003cButton\u003eStacked button\u003c/Button\u003e\n  \u003cButton color=\"title\"\u003eCustom color\u003c/Button\u003e\n  \u003cButton height={6}\u003eCustom height\u003c/Button\u003e\n\u003c/HStack\u003e\n```\n\n## Vertical stack\n\n`VStack` displays buttons in a row without having to specify each button's position manually. You can also override button colors and sizes.\n\n### Import\n\n```ts\nimport { VStack } from 'react-node-insim';\n```\n\n### Usage\n\n\u003cimg src=\"docs/vertical-stack.png\" alt=\"Vertical stack\" /\u003e\n\n```tsx\n\u003cVStack top={10} left={20} width={7} height={4} variant=\"dark\"\u003e\n  \u003cButton\u003eStacked button\u003c/Button\u003e\n  \u003cButton color=\"title\"\u003eCustom color\u003c/Button\u003e\n  \u003cButton height={6}\u003eCustom height\u003c/Button\u003e\n\u003c/VStack\u003e\n```\n\n## Flex\n\nFlex layout displays buttons in a row or column with flexbox options.\n\n### Import\n\n```ts\nimport { Flex } from 'react-node-insim';\n```\n\n### Usage\n\n\u003cimg src=\"docs/flex.png\" alt=\"Flex\" /\u003e\n\n```tsx\n\u003cFlex\n  top={10}\n  left={20}\n  width={36}\n  height={16}\n  alignItems=\"center\"\n  justifyContent=\"space-evenly\"\n  background=\"dark\"\n  backgroundColor=\"light\"\n\u003e\n  \u003cButton width={8} height={4}\u003e\n    Left\n  \u003c/Button\u003e\n  \u003cButton width={10} height={6}\u003e\n    Center\n  \u003c/Button\u003e\n  \u003cButton width={8} height={4}\u003e\n    Right\n  \u003c/Button\u003e\n\u003c/Flex\u003e\n```\n\n## Grid\n\nGrid layout displays buttons in a grid.\n\n### Import\n\n```ts\nimport { Grid, GridButton } from 'react-node-insim';\n```\n\n### Usage\n\n\u003cimg src=\"docs/grid.png\" alt=\"Grid\" /\u003e\n\n```tsx\n\u003cGrid\n  top={30}\n  left={40}\n  width={30}\n  height={30}\n  background=\"dark\"\n  backgroundColor=\"light\"\n  gridTemplateColumns=\"1fr 2fr 1fr\"\n  gridTemplateRows=\"1fr 3fr 2fr\"\n  gridColumnGap={1}\n  gridRowGap={1}\n\u003e\n  \u003cGridButton\u003e1\u003c/GridButton\u003e\n  \u003cGridButton\n    gridColumnStart={2}\n    gridRowStart={1}\n    gridRowEnd={3}\n    color=\"title\"\n    background=\"light\"\n  \u003e\n    2\n  \u003c/GridButton\u003e\n  \u003cGridButton\n    gridColumnStart={3}\n    gridColumnEnd={3}\n    gridRowStart={1}\n    gridRowEnd={3}\n  \u003e\n    3\n  \u003c/GridButton\u003e\n  \u003cGridButton alignSelf=\"end\" height={10}\u003e\n    4\n  \u003c/GridButton\u003e\n  \u003cGridButton gridColumnStart={1} gridColumnEnd={4}\u003e\n    5\n  \u003c/GridButton\u003e\n\u003c/Grid\u003e\n```\n\n## Toggle button\n\nA button that can be toggled on and off by clicking it.\n\n### Import\n\n```ts\nimport { ToggleButton } from 'react-node-insim';\n```\n\n### Usage\n\n\u003cimg src=\"docs/toggle-button.gif\" alt=\"Toggle button being toggled on and off\" /\u003e\n\n```tsx\nfunction App() {\n  const [isOn, setIsOn] = useState(false);\n\n  return (\n    \u003cToggleButton\n      top={100}\n      left={80}\n      width={12}\n      height={6}\n      isOn={isOn}\n      onToggle={setIsOn}\n    \u003e\n      Toggle\n    \u003c/ToggleButton\u003e\n  );\n}\n```\n\n#### Variants\n\nUse the `variant` prop to change the button's background style. You can use `light` or `dark`. If you don't specify a variant, `light` will be used.\n\n\u003cimg src=\"docs/toggle-button-variants.png\" alt=\"Toggle button variants\" /\u003e\n\n```tsx\n\u003c\u003e\n  \u003cToggleButton variant=\"light\" top={100} left={40} width={12} height={6}\u003e\n    Toggle\n  \u003c/ToggleButton\u003e\n  \u003cToggleButton variant=\"dark\" top={100} left={53} width={12} height={6}\u003e\n    Toggle\n  \u003c/ToggleButton\u003e\n\u003c/\u003e\n```\n\n#### Disabled state\n\nUse the `isDisabled` prop to prevent toggling the button on/off.\n\n\u003cimg src=\"docs/toggle-button-disabled.png\" alt=\"Enabled and disabled toggle buttons\" /\u003e\n\n```tsx\n\u003c\u003e\n  \u003cToggleButton\n    isDisabled={false}\n    variant=\"light\"\n    top={100}\n    left={40}\n    width={12}\n    height={6}\n  \u003e\n    Enabled\n  \u003c/ToggleButton\u003e\n  \u003cToggleButton\n    isDisabled\n    variant=\"light\"\n    top={100}\n    left={53}\n    width={12}\n    height={6}\n  \u003e\n    Disabled\n  \u003c/ToggleButton\u003e\n  \u003cToggleButton\n    isDisabled={false}\n    variant=\"dark\"\n    top={106}\n    left={40}\n    width={12}\n    height={6}\n  \u003e\n    Enabled\n  \u003c/ToggleButton\u003e\n  \u003cToggleButton\n    isDisabled\n    variant=\"dark\"\n    top={106}\n    left={53}\n    width={12}\n    height={6}\n  \u003e\n    Disabled\n  \u003c/ToggleButton\u003e\n\u003c/\u003e\n```\n\n## Toggle button group\n\nA group of buttons that can be toggled on and off by clicking them.\n\n### Import\n\n```ts\nimport { ToggleButtonGroup } from 'react-node-insim';\n```\n\n### Usage\n\n\u003cimg src=\"docs/toggle-button-group.gif\" alt=\"Toggle button group\" /\u003e\n\n```tsx\nconst options = [\n  { label: 'low', value: 1 },\n  { label: 'medium', value: 2 },\n  { label: 'high', value: 3 },\n];\n\nfunction App() {\n  const [selectedOption, setSelectedOption] = useState(options[0]);\n\n  return (\n    \u003cToggleButtonGroup\n      top={30}\n      left={50}\n      width={36}\n      height={6}\n      options={options}\n      selectedOption={selectedOption}\n      onChange={setSelectedOption}\n    /\u003e\n  );\n}\n```\n\n## Text box\n\nA text box whose content can span multiple rows. If the content is too long, the text box will show a scrollbar.\n\n### Import\n\n```ts\nimport { TextBox } from 'react-node-insim';\n```\n\n### Usage\n\n\u003cimg src=\"docs/text-box.gif\" alt=\"Text box\" /\u003e\n\n```tsx\n\u003cTextBox\n  top={40}\n  left={50}\n  cols={20}\n  rows={4}\n  width={20}\n  rowHeight={4}\n  variant=\"light\"\n\u003e\n  Hello world this is a text box lorem ipsum dolor sit amet consectetur\n  adipisicing elitrea lorem ipsum dolor sit amet consectetur adipisicing elit\n\u003c/TextBox\u003e\n```\n\n## Hooks\n\n### `useOnConnect`\n\nExecute code after the InSim app has been connected.\n\nThe first parameter is an `IS_VER` packet callback executed when `IS_VER` is received upon successful InSim connection to LFS.\n\n```tsx\nimport { useOnConnect } from 'react-node-insim';\n\nfunction App() {\n  useOnConnect((packet, inSim) =\u003e {\n    console.log(`Connected to LFS ${packet.Product} ${packet.Version}`);\n    inSim.send(new IS_MST({ Msg: `React Node InSim connected` }));\n  });\n\n  return null;\n}\n```\n\n### `useOnDisconnect`\n\nExecute code after the InSim app has been disconnected.\n\nThe first parameter is the \"disconnect\" event callback from Node InSim.\n\n```tsx\nimport { useOnDisconnect } from 'react-node-insim';\n\nfunction App() {\n  useOnDisconnect(() =\u003e {\n    console.log('Disconnected from LFS');\n  });\n\n  return null;\n}\n```\n\n### `useOnPacket`\n\nExecute code when an InSim packet is received\n\n```tsx\nimport { useOnPacket } from 'react-node-insim';\n\nfunction App() {\n  useOnPacket(PacketType.ISP_NCN, (packet) =\u003e {\n    console.log(`New connection: ${packet.UName}`);\n  });\n\n  return null;\n}\n```\n\n### `useConnections`\n\nGet a live list of all connected guests.\n\n```tsx\nimport { useConnections } from 'react-node-insim';\n\nfunction App() {\n  const connections = useConnections();\n\n  return (\n    \u003cVStack background=\"dark\" top={10} left={10} width={20} height={4}\u003e\n      {connections.map((connection) =\u003e (\n        \u003cButton key={connection.UCID}\u003e{connection.UName}\u003c/Button\u003e\n      ))}\n    \u003c/VStack\u003e\n  );\n}\n```\n\n### `usePlayers`\n\nGet a live list of all players on track.\n\n```tsx\nimport { usePlayers } from 'react-node-insim';\n\nfunction App() {\n  const players = usePlayers();\n\n  return (\n    \u003cVStack background=\"dark\" top={10} left={10} width={20} height={4}\u003e\n      {players.map((player) =\u003e (\n        \u003cButton key={player.PLID}\u003e{player.PName}\u003c/Button\u003e\n      ))}\n    \u003c/VStack\u003e\n  );\n}\n```\n\n### `useRaceControlMessage`\n\nSend a race control message (RCM) to a connection or a player.\n\n```tsx\nimport { useRaceControlMessage } from 'react-node-insim';\n\nfunction App() {\n  const { sendRaceControlMessageToConnection, sendRaceControlMessageToPlayer } =\n    useRaceControlMessage();\n\n  return (\n    \u003c\u003e\n      \u003cButton\n        top={5}\n        left={10}\n        width={15}\n        height={5}\n        onClick={(packet) =\u003e {\n          sendRaceControlMessageToConnection(\n            packet.UCID,\n            'Hello from React Node InSim',\n            2000,\n          );\n        }}\n      \u003e\n        Send message to a connection\n      \u003c/Button\u003e\n      \u003cButton\n        top={10}\n        left={10}\n        width={15}\n        height={5}\n        onClick={(packet) =\u003e {\n          sendRaceControlMessageToPlayer(\n            12, // PLID\n            'Hello from React Node InSim',\n            2000,\n          );\n        }}\n      \u003e\n        Send message to a player\n      \u003c/Button\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n### `useInSim`\n\nAccess to Node InSim API of the current InSim client instance.\n\n```tsx\nimport { useInSim } from 'react-node-insim';\n\nfunction App() {\n  const inSim = useInSim();\n\n  useEffect(() =\u003e {\n    inSim.send(new IS_MST({ Msg: 'App mounted' }));\n  }, []);\n\n  return null;\n}\n```\n\n## Scopes\n\nIf you needed to show personalised buttons for each connection or each human player on track, you would need to map over the list of connections/players and pass the correct UCIDs to each button manually. Scopes help in such use cases.\n\n### Connection scope\n\nYou can show different buttons to each connection by wrapping a sub-tree in a `ConnectionScopeProvider`, then using the `useConnectionScope` hook anywhere within that sub-tree to access the connection object.\n\nYou don't need to specify the button's UCID in the scope - the correct UCID will be injected automatically.\n\n```tsx\nimport { \n  Button, \n  ConnectionScopeProvider, \n  useConnectionScope,\n} from 'react-node-insim';\n\nfunction App() {\n  return (\n    \u003cConnectionScopeProvider\u003e\n      \u003cUserNameButton /\u003e\n    \u003c/ConnectionScopeProvider\u003e\n  );\n}\n\nfunction UserNameButton() {\n  const { UName } = useConnectionScope();\n\n  return (\n    \u003cButton top={0} left={80} height={5} width={25}\u003e\n      {UName}\n    \u003c/Button\u003e\n  );\n}\n```\n\n### Human player scope\n\nYou can show different buttons to each human player on track by wrapping a sub-tree in a `HumanPlayerScopeProvider`, then using the `useHumanPlayerScope` hook anywhere within that sub-tree to access the player object.\n\nYou don't need to specify the button's UCID in the scope - the correct UCID will be injected automatically.\n\n```tsx\nimport {\n  Button,\n  HumanPlayerScopeProvider,\n  useHumanPlayerScope,\n} from 'react-node-insim';\n\nfunction App() {\n  return (\n    \u003cHumanPlayerScopeProvider\u003e\n      \u003cPlayerNameButton /\u003e\n    \u003c/HumanPlayerScopeProvider\u003e\n  );\n}\n\nfunction UserNameButton() {\n  const { PName } = useHumanPlayerScope();\n\n  return (\n    \u003cButton top={0} left={80} height={5} width={25}\u003e\n      {PName}\n    \u003c/Button\u003e\n  );\n}\n```\n\n\n### Global scope\n\nYou can show the same set of buttons to all connections wrapping a sub-tree in a `GlobalScopeProvider`.\n\nYou don't need to specify the button's UCID in the scope - the correct UCID value of 255 will be injected automatically.\n\n```tsx\nimport { Button, GlobalScopeProvider } from 'react-node-insim';\n\nfunction App() {\n  return (\n    \u003cGlobalScopeProvider\u003e\n      \u003cButton\n        top={0}\n        left={80}\n        height={5}\n        width={40}\n      \u003e\n        React Node InSim\n      \u003c/Button\u003e\n    \u003c/GlobalScopeProvider\u003e\n  );\n}\n```\n\n## Development\n\n### Requirements\n\n- [Node.js 18](https://nodejs.org/)\n- [Yarn v1](https://classic.yarnpkg.com/)\n- [Live for Speed](https://www.lfs.net/)\n\n### Installation\n\n```shell\nyarn\n```\n\n### Run example app\n\n```shell\nyarn start\n```\n\n### Lint code\n\n```shell\nyarn lint\n```\n\n### Format code\n\n```shell\nyarn format\n```\n\n---\n\n![React Node Insim - An open source project by Sim Broadcasts](https://simbroadcasts.tv/assets/node-insim/react-node-insim-footer.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimbroadcasts%2Freact-node-insim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimbroadcasts%2Freact-node-insim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimbroadcasts%2Freact-node-insim/lists"}