{"id":25013877,"url":"https://github.com/code4recovery/tsml-ui","last_synced_at":"2025-09-11T17:07:08.877Z","repository":{"id":37582611,"uuid":"141331151","full_name":"code4recovery/tsml-ui","owner":"code4recovery","description":"Embeddable recovery meeting finder","archived":false,"fork":false,"pushed_at":"2025-06-18T17:28:31.000Z","size":45680,"stargazers_count":24,"open_issues_count":7,"forks_count":18,"subscribers_count":16,"default_branch":"main","last_synced_at":"2025-06-18T18:35:00.455Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://tsml-ui.code4recovery.org/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/code4recovery.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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,"zenodo":null}},"created_at":"2018-07-17T18:57:53.000Z","updated_at":"2025-06-18T17:28:35.000Z","dependencies_parsed_at":"2023-12-26T17:43:05.501Z","dependency_job_id":"45c25a7c-4e3a-4bd8-984d-16f8619bec78","html_url":"https://github.com/code4recovery/tsml-ui","commit_stats":null,"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/code4recovery/tsml-ui","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code4recovery%2Ftsml-ui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code4recovery%2Ftsml-ui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code4recovery%2Ftsml-ui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code4recovery%2Ftsml-ui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/code4recovery","download_url":"https://codeload.github.com/code4recovery/tsml-ui/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code4recovery%2Ftsml-ui/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261390861,"owners_count":23151653,"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":[],"created_at":"2025-02-05T07:16:35.910Z","updated_at":"2025-09-11T17:07:08.865Z","avatar_url":"https://github.com/code4recovery.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TSML UI \u0026nbsp; [![Coverage Status](https://coveralls.io/repos/github/code4recovery/tsml-ui/badge.svg?branch=main)](https://coveralls.io/github/code4recovery/tsml-ui?branch=main)\n\nTSML UI (12 Step Meeting List User Interface) is an interactive meeting finder makes the [12 Step Meeting List](https://github.com/code4recovery/12-step-meeting-list) interface available for use on any web page, regardless of platform.\n\nHere are two demos:\n\n- [San Francisco / Marin](https://aasfmarin.org/meetings) (uses a custom database as a data source)\n\n- [Santa Cruz](https://aasantacruz.org/meeting-guide/) (WordPress / 12 Step Meeting List data source)\n\nTo use TSML UI on your website you only need to add some HTML to your web page. To get started, use our [configuration instructions](https://tsml-ui.code4recovery.org).\n\n## Configure\n\n### Enable \"Near Me\" mode\n\nYou don't need to do anything other than enable HTTPS on your website. To ensure all users see this functionality, make sure that anyone who enters a `http://` address for your site is redirected to the `https://` address.\n\n### Add custom types\n\nHere is an example of extending the `tsml_react_config` object to include a definition for an additional meeting type.\n\n```js\nvar tsml_react_config = {\n  strings: {\n    en: {\n      types: {\n        BEACH: 'Beach Meeting',\n      },\n    },\n  },\n};\n```\n\nA.A. groups that wish to participate in the Meeting Guide app should be careful not to repurpose types already in use. A full list of A.A. meeting types can be found in the [Meeting Guide format spec](https://github.com/code4recovery/spec).\n\n### Override type descriptions\n\nA.A. meeting type descriptions are automatically applied to Open and Closed types. These can be unset or overwritten as needed.\n\n```js\nvar tsml_react_config = {\n  strings: {\n    en: {\n      type_descriptions: {\n        O: 'This is a custom Open description',\n        C: undefined, //this type description has been unset\n      },\n    },\n  },\n};\n```\n\n### Override type defaults\n\nIf you wanted to add a list that is pre-filtered to a single type, \"Women\" in this example, you can add this code:\n\n```js\nvar tsml_react_config = {\n  defaults: { type: ['women'] },\n  show: {\n    controls: false,\n    title: false,\n  },\n};\n```\n\n### Use kilometers\n\nDistances can be calculated in miles (`mi`) or kilometers (`km`).\n\n```js\nvar tsml_react_config = {\n  distance_unit: 'km',\n};\n```\n\n### Disable Add to Calendar button\n\nYou can disable the add to calendar button if needed.\n\n```js\nvar tsml_react_config = {\n  calendar_enabled: false,\n};\n```\n\n### Change the map style\n\nOpen Street Maps can be styled by picking a different tile layer URL and attribution and adding them to the `tsml_react_config` object. There are several free and paid tile layers [listed here](https://leaflet-extras.github.io/leaflet-providers/preview/).\n\n```js\nvar tsml_react_config = {\n  map: {\n    tiles: {\n      attribution:\n        '\u0026copy; \u003ca href=\"https://www.stadiamaps.com/\" target=\"_blank\"\u003eStadia Maps\u003c/a\u003e \u0026copy; \u003ca href=\"https://openmaptiles.org/\" target=\"_blank\"\u003eOpenMapTiles\u003c/a\u003e \u0026copy; \u003ca href=\"https://www.openstreetmap.org/copyright\"\u003eOpenStreetMap\u003c/a\u003e contributors',\n      url: 'https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png',\n    },\n  },\n};\n```\n\nYou can optionally specify a `tiles_dark` attribute which will be used when `prefers-color-scheme: dark`.\n\n```js\nvar tsml_react_config = {\n  map: {\n    tiles_dark: {\n      attribution: '\u0026copy; ...',\n      url: 'https://...',\n    },\n  },\n};\n```\n\nIf you prefer the Mapbox map appearance (we switched away from Mapbox in May 2025), you can add this to your site (just replace `\u003cpk.your.access.token\u003e` with your Mabpox access token):\n\n```js\nvar tsml_react_config = {\n  map: {\n    tiles: {\n      attribution: `Map data \u0026copy; \u003ca href=\"https://www.openstreetmap.org/\"\u003eOpenStreetMap\u003c/a\u003e, Imagery © \u003ca href=\"https://www.mapbox.com/\"\u003eMapbox\u003c/a\u003e`,\n      url: 'https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=\u003cpk.your.access.token\u003e',\n    },\n    tiles_dark: {\n      attribution: `Map data \u0026copy; \u003ca href=\"https://www.openstreetmap.org/\"\u003eOpenStreetMap\u003c/a\u003e, Imagery © \u003ca href=\"https://www.mapbox.com/\"\u003eMapbox\u003c/a\u003e`,\n      url: 'https://api.mapbox.com/styles/v1/mapbox/dark-v10/tiles/{z}/{x}/{y}?access_token=\u003cpk.your.access.token\u003e',\n    },\n  },\n};\n```\n\nMarkers can be adjusted by supplying an alternate SVG image:\n\n```js\nvar tsml_react_config = {\n  map: {\n    markers: {\n      location: {\n        html: `\u003csvg…`,\n        height: 48,\n        width: 12,\n      },\n    },\n  },\n};\n```\n\n### Customize theme colors\n\nYou can use CSS variables to customize TSML UI’s appearance. Here are the defaults:\n\n```css\n#tsml-ui {\n  --alert-background: #faf4e0;\n  --alert-text: #998a5e;\n  --background: #fff;\n  --border-radius: 4px;\n  --focus: #0d6efd40;\n  --font-family: system-ui, -apple-system, sans-serif;\n  --font-size: 16px;\n  --in-person: #146c43;\n  --inactive: #b02a37;\n  --link: #0d6efd;\n  --online: #0a58ca;\n  --online-background-image: url(https://images.unsplash.com/photo-1588196749597-9ff075ee6b5b?crop=entropy\u0026cs=tinysrgb\u0026fit=crop\u0026fm=jpg\u0026h=1440\u0026ixid=MnwxfDB8MXxhbGx8fHx8fHx8fHwxNjIyMTIzODkw\u0026ixlib=rb-1.2.1\u0026q=80\u0026utm_campaign=api-credit\u0026utm_medium=referral\u0026utm_source=unsplash_source\u0026w=1920);\n  --text: #212529;\n}\n```\n\nOnly specify the variables you wish to override in your code. Hex values (`#123456`) must be used when specifiying colors.\n\n### Dark mode\n\nIf your website theme supports responsive dark mode, TSML UI can match it with a media query: In [this demo](https://tsml-ui.code4recovery.org/tests/aasanjose.html) the background will be white if the user prefers a light appearance, and black if they prefer dark.\n\n```css\n@media (prefers-color-scheme: dark) {\n  #tsml-ui {\n    --background: #000;\n    --color: #fff;\n    --link: #7bc8ff;\n  }\n}\n```\n\n### Change the online background image\n\nThis image will be shown instead of a map for online meetings. Should be roughly 2000px wide and tall.\n\n```css\n#tsml-ui {\n  --online-background-image: url(/path/to/image.jpg);\n}\n```\n\nTo remove:\n\n```css\n#tsml-ui {\n  --online-background-image: none;\n}\n```\n\n## Frequently asked questions\n\n### How do timezones work?\n\nIf you are only listing meetings in a single timezone, e.g. Philadelphia, PA, then you should specify a `data-timezone` attribute in your embed code. This must be in the proper [IANA timezone format](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) e.g. `America/New_York`. TSML UI will assume that any meetings without a specified timezone are in that zone.\n\nHowever if your site lists meetings in a variety of timezones, and you have a timezone key/column in your meeting data, then you may omit the `data-timezone` attribute and times will be translated into the user's timezone.\n\nNote: The WordPress plugin 12 Step Meeting List does not yet support timezone keys in meeting data.\n\n### How are metatypes like \"Active\" and \"Online\" calculated?\n\nMetatypes are types that are not specified explicitly in the data, they are inferred from the data based on this logic:\n\n- A meeting is considered `In-Person` if it doesn't have a type of `Location Temporarily Closed` and it has a specific street address.\n\n- A meeting is considered `Online` if it has a `conference_url` that matches our recognized formats and/or it has a `conference_phone`\n\n- A meeting is considered `Active` if it's `In-Person` or `Online`, otherwise it's `Inactive`.\n\n### Why no \"Hybrid\" type?\n\nThis app exists to help people find A.A. meetings, and after much discussion we decided that Hybrid was not a useful filter type for that purpose. We believe that people on the whole do not set out looking for a hybrid meeting, they simply want to know whether their online or in-person meeting happens to be hybrid. They will know this by its appearance in the list.\n\nSecond, while we can infer that a meeting is \"online\" if there is a Zoom URL (for example) in the listing, the app should not assume that, when there are online and in-person options, that means it is an actual \"hybrid\" meeting with a video screen and speakers in the room.\n\nWeb servants may [add their own meeting types](#add-custom-types) of course.\n\n### What is Speaker/Discussion?\n\nWhen meetings are tagged both Speaker (`SP`) and Discussion (`D`), TSML UI merges them into a combined Speaker/Discussion type. This enables users to use existing filters to locate Speaker-only and Discussion-only meetings.\n\n### Can I import TSML UI from NPM for use in a NextJS or Gatsby project?\n\nNot yet! Please open a pull request and walk us through the process of adding it to NPM and we'll give it a shot.\n\n## Contributing\n\nContributions are welcome. Ideally, please join [Code for Recovery](https://code4recovery.org/) (we have no dues or fees) beforehand to discuss your proposed changes, or at a minimum file an issue. (The one exception: language translations do not need an issue beforehand.)\n\nHere are the steps to follow when developing:\n\n1. clone (or, if you are not a member, fork and clone) this repository\n1. create a branch for your changes\n1. run `npm i` in the project folder (install NPM if it is not installed)\n1. run `npm run serve` in one terminal window (or use another solution to serve files locally)\n1. run `npx mix watch` in another terminal window (this updates files in development as you change them)\n1. confirm your changes at, for example, `http://localhost:3000/tests/aasanjose` (there are a number of examples in that folder)\n\nWhen you are ready to make a PR:\n\n1. clean up your diff, try to change as few lines as possible\n1. run prettier locally to autoformat your files\n1. alphabetize things like component props and CSS rules (if applicable)\n1. run `npx mix --production` to minify files\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcode4recovery%2Ftsml-ui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcode4recovery%2Ftsml-ui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcode4recovery%2Ftsml-ui/lists"}