Author a workspace symbol
A workspace symbol adds equipment that the built-in catalog lacks to every drawing in a workspace. This guide writes one in a pulled workspace. The definition fields and hooks are described in Workspace symbols.
Create the definition
Section titled “Create the definition”- Check the built-in catalog first:
builtinGraphicsSymbolslists the symbols of the installed package version, andgraphics.symbols.searchthose of the server, with their properties and ports. - Create a
.tsxfile undersrc/symbols/, one file per symbol. - Default-export a
defineGraphicsSymboldefinition with a uniqueid, anamefactory, the authoredwidthandheight,stretch,ports, apropertiesfactory, and theComponent. Do not start the ID withhmi., which is reserved for built-in symbols. Never reuse an ID for an unrelated object: placements of the old symbol render the new one.
Declare the properties
Section titled “Declare the properties”- Declare a closed TypeBox object with a valid default for every property.
- Label each field and literal choice with
title, using translated messages. - Bound numeric inputs with
minimumandmaximum, but not a reading that is only displayed as text: bound values are clamped. - Mark a color with
graphicsColor: true, and text that viewers read withgraphicsText: true. - To receive
unknownwhen a state input is unavailable, add anunknownliteral to its union. - For a reading with a unit, name the unit property with
unitProperty. - Return the name, action labels, and properties from factories, such as
name: () => t("key"), so their text follows the active language.
Draw the artwork
Section titled “Draw the artwork”- Return your own
<svg>root with aviewBox, or HTML controls. - Create clip, gradient, and mask IDs with
useId, so placements do not collide. - For a stretchable symbol (
stretch: true), draw the artwork from the viewport size ofuseGraphicsSymbolViewport(), so each drawn connection stays at its port. Anchor ports withanchorXoranchorYwhere they keep a distance from an edge. - Lay out resizable controls from the viewport’s
widthandheight, so labels are not distorted. - Use
GraphicsSymbolTextinstead of<text>,GraphicsSymbolCaptionfor a label along the bottom edge,GraphicsSymbolCaptionBlockfor the tag and status lines, andGraphicsSymbolUprightfor a display face, so text stays upright when the placement turns or mirrors.
Show live quality
Section titled “Show live quality”- Read each property’s state with
useGraphicsSymbolProperty(name). - Leave out an optional reading that is not
assigned, instead of showing its default as a value. - Render an
unknownproperty as unknown: ”—” for a reading, withformatGraphicsSymbolReading, and an explicit unknown indication for a state. - Pause motion that the component manages while
editingis true, or while a property that drives it is notfresh. Respect the browser’s reduced-motion preference.
Add named controls
Section titled “Add named controls”- Declare control names in
actions, such asactions: () => ({ activate: "Pump command" }). - In the component, connect a
ButtontouseGraphicsSymbolAction("activate"). Do not add a second handler that writes to equipment. - When neighbors can sit against the symbol, such as rooms sharing a wall, declare its
body, so whole-object clicks land only on the equipment itself.
Describe it in the catalog
Section titled “Describe it in the catalog”- Add a
catalogfactory withcategory,industries,description,keywords, andexamples. Addfamily,variants,essential,units, andgridwhere they apply. - Give each port a
roleand adirection. - Check the result with
serializeGraphicsSymbolCatalog(definitions, locale).
Publish the symbol
Section titled “Publish the symbol”- Run
pnpm buildand fix any errors. - Push the workspace. Every dashboard picks up the symbol on reload.
- Do not rename or remove a property or port that placements use. If you must, find the placements that show an incompatibility and repair them.