tangram-core 0.2.0__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. tangram_core/App.vue +335 -0
  2. tangram_core/__init__.py +5 -0
  3. tangram_core/__main__.py +141 -0
  4. tangram_core/_core.cp313-win_amd64.pyd +0 -0
  5. tangram_core/_core.pyi +38 -0
  6. tangram_core/api.ts +456 -0
  7. tangram_core/backend.py +335 -0
  8. tangram_core/colour.ts +71 -0
  9. tangram_core/config.py +122 -0
  10. tangram_core/dist-frontend/aggregation-layers.js +521 -0
  11. tangram_core/dist-frontend/aggregation-layers.js.map +1 -0
  12. tangram_core/dist-frontend/assets/_commonjsHelpers-CqkleIqs.js +2 -0
  13. tangram_core/dist-frontend/assets/_commonjsHelpers-CqkleIqs.js.map +1 -0
  14. tangram_core/dist-frontend/assets/array-utils-flat-wyE8tIYR.js +11 -0
  15. tangram_core/dist-frontend/assets/array-utils-flat-wyE8tIYR.js.map +1 -0
  16. tangram_core/dist-frontend/assets/assert-hrfsarFU.js +3 -0
  17. tangram_core/dist-frontend/assets/assert-hrfsarFU.js.map +1 -0
  18. tangram_core/dist-frontend/assets/b612-latin-400-italic-DePNXA0a.woff +0 -0
  19. tangram_core/dist-frontend/assets/b612-latin-400-italic-a-4GLPtl.woff2 +0 -0
  20. tangram_core/dist-frontend/assets/b612-latin-400-normal-CC98FVm_.woff2 +0 -0
  21. tangram_core/dist-frontend/assets/b612-latin-400-normal-JbZ7xwUX.woff +0 -0
  22. tangram_core/dist-frontend/assets/b612-latin-700-normal-B_Snq1wd.woff +0 -0
  23. tangram_core/dist-frontend/assets/b612-latin-700-normal-BinQrnoB.woff2 +0 -0
  24. tangram_core/dist-frontend/assets/clip-extension-DTCP51Ak.js +26 -0
  25. tangram_core/dist-frontend/assets/clip-extension-DTCP51Ak.js.map +1 -0
  26. tangram_core/dist-frontend/assets/color-CUNNsFV-.js +17 -0
  27. tangram_core/dist-frontend/assets/color-CUNNsFV-.js.map +1 -0
  28. tangram_core/dist-frontend/assets/cube-geometry-CzJ_uBWa.js +2 -0
  29. tangram_core/dist-frontend/assets/cube-geometry-CzJ_uBWa.js.map +1 -0
  30. tangram_core/dist-frontend/assets/deep-equal-uriyKJca.js +2 -0
  31. tangram_core/dist-frontend/assets/deep-equal-uriyKJca.js.map +1 -0
  32. tangram_core/dist-frontend/assets/fly-to-interpolator-DlKiy9_S.js +2 -0
  33. tangram_core/dist-frontend/assets/fly-to-interpolator-DlKiy9_S.js.map +1 -0
  34. tangram_core/dist-frontend/assets/geojson-layer-CLhXLxdI.js +1010 -0
  35. tangram_core/dist-frontend/assets/geojson-layer-CLhXLxdI.js.map +1 -0
  36. tangram_core/dist-frontend/assets/globe-view-DKhftlA1.js +94 -0
  37. tangram_core/dist-frontend/assets/globe-view-DKhftlA1.js.map +1 -0
  38. tangram_core/dist-frontend/assets/globe-viewport-CPES4D4P.js +2 -0
  39. tangram_core/dist-frontend/assets/globe-viewport-CPES4D4P.js.map +1 -0
  40. tangram_core/dist-frontend/assets/image-loader-ClbNCMXW.js +2 -0
  41. tangram_core/dist-frontend/assets/image-loader-ClbNCMXW.js.map +1 -0
  42. tangram_core/dist-frontend/assets/inconsolata-latin-400-normal-DTZQ6lD6.woff2 +0 -0
  43. tangram_core/dist-frontend/assets/inconsolata-latin-400-normal-HYADljCo.woff +0 -0
  44. tangram_core/dist-frontend/assets/inconsolata-latin-700-normal-ByjKuJjN.woff2 +0 -0
  45. tangram_core/dist-frontend/assets/inconsolata-latin-700-normal-DzgUY3Rl.woff +0 -0
  46. tangram_core/dist-frontend/assets/inconsolata-latin-ext-400-normal-BaHVOdFB.woff2 +0 -0
  47. tangram_core/dist-frontend/assets/inconsolata-latin-ext-400-normal-yvPjCxxx.woff +0 -0
  48. tangram_core/dist-frontend/assets/inconsolata-latin-ext-700-normal-D0Kpgs_9.woff2 +0 -0
  49. tangram_core/dist-frontend/assets/inconsolata-latin-ext-700-normal-Dlt-daqV.woff +0 -0
  50. tangram_core/dist-frontend/assets/inconsolata-vietnamese-400-normal-ByiM2lek.woff +0 -0
  51. tangram_core/dist-frontend/assets/inconsolata-vietnamese-400-normal-DfC_iMic.woff2 +0 -0
  52. tangram_core/dist-frontend/assets/inconsolata-vietnamese-700-normal-DLCFFAUf.woff +0 -0
  53. tangram_core/dist-frontend/assets/inconsolata-vietnamese-700-normal-DuasYmn8.woff2 +0 -0
  54. tangram_core/dist-frontend/assets/index-UPPakSLR.css +1 -0
  55. tangram_core/dist-frontend/assets/index-r8T0kY2p.js +821 -0
  56. tangram_core/dist-frontend/assets/index-r8T0kY2p.js.map +1 -0
  57. tangram_core/dist-frontend/assets/layer-DO63TrsS.js +555 -0
  58. tangram_core/dist-frontend/assets/layer-DO63TrsS.js.map +1 -0
  59. tangram_core/dist-frontend/assets/layer-extension-CZ3zsHuN.js +2 -0
  60. tangram_core/dist-frontend/assets/layer-extension-CZ3zsHuN.js.map +1 -0
  61. tangram_core/dist-frontend/assets/mesh-layers-BSECKarm.js +1123 -0
  62. tangram_core/dist-frontend/assets/mesh-layers-BSECKarm.js.map +1 -0
  63. tangram_core/dist-frontend/assets/orthographic-viewport-CzZmHDEZ.js +2 -0
  64. tangram_core/dist-frontend/assets/orthographic-viewport-CzZmHDEZ.js.map +1 -0
  65. tangram_core/dist-frontend/assets/pick-layers-pass-xhWsgZtf.js +2 -0
  66. tangram_core/dist-frontend/assets/pick-layers-pass-xhWsgZtf.js.map +1 -0
  67. tangram_core/dist-frontend/assets/project-CrvReKGW.js +760 -0
  68. tangram_core/dist-frontend/assets/project-CrvReKGW.js.map +1 -0
  69. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-400-italic-4qS3_zkX.woff2 +0 -0
  70. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-400-italic-CDK-EZBY.woff +0 -0
  71. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-400-normal-Bgns473E.woff +0 -0
  72. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-400-normal-_T2aQlWs.woff2 +0 -0
  73. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-500-normal-CvEVpWxD.woff +0 -0
  74. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-500-normal-s4PklZE0.woff2 +0 -0
  75. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-700-normal-9RN-Z7cI.woff2 +0 -0
  76. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-700-normal-BGMkBBYx.woff +0 -0
  77. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-400-italic-C7erd-g8.woff +0 -0
  78. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-400-italic-DR5R5TWx.woff2 +0 -0
  79. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-400-normal-DGo1Ayjq.woff2 +0 -0
  80. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-400-normal-WtM1l1qc.woff +0 -0
  81. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-500-normal-C8FNIdXm.woff2 +0 -0
  82. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-500-normal-TLDmfi3Q.woff +0 -0
  83. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-700-normal-CTXjXnze.woff2 +0 -0
  84. tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-700-normal-CWPRiRXS.woff +0 -0
  85. tangram_core/dist-frontend/assets/roboto-condensed-greek-400-italic-CR6qj4Z4.woff2 +0 -0
  86. tangram_core/dist-frontend/assets/roboto-condensed-greek-400-italic-DHRaIs10.woff +0 -0
  87. tangram_core/dist-frontend/assets/roboto-condensed-greek-400-normal-D5vBSIyg.woff2 +0 -0
  88. tangram_core/dist-frontend/assets/roboto-condensed-greek-400-normal-FabMgVmk.woff +0 -0
  89. tangram_core/dist-frontend/assets/roboto-condensed-greek-500-normal-BIN62cw9.woff +0 -0
  90. tangram_core/dist-frontend/assets/roboto-condensed-greek-500-normal-Hsn-wDIp.woff2 +0 -0
  91. tangram_core/dist-frontend/assets/roboto-condensed-greek-700-normal-89Up2Xly.woff +0 -0
  92. tangram_core/dist-frontend/assets/roboto-condensed-greek-700-normal-DWMOA2VK.woff2 +0 -0
  93. tangram_core/dist-frontend/assets/roboto-condensed-latin-400-italic-D_BR-3LG.woff2 +0 -0
  94. tangram_core/dist-frontend/assets/roboto-condensed-latin-400-italic-om57GXsO.woff +0 -0
  95. tangram_core/dist-frontend/assets/roboto-condensed-latin-400-normal-BICmKrXV.woff2 +0 -0
  96. tangram_core/dist-frontend/assets/roboto-condensed-latin-400-normal-D2e7XwB1.woff +0 -0
  97. tangram_core/dist-frontend/assets/roboto-condensed-latin-500-normal-3p2daRJW.woff2 +0 -0
  98. tangram_core/dist-frontend/assets/roboto-condensed-latin-500-normal-Dc9bsamC.woff +0 -0
  99. tangram_core/dist-frontend/assets/roboto-condensed-latin-700-normal-BOl6B_hI.woff +0 -0
  100. tangram_core/dist-frontend/assets/roboto-condensed-latin-700-normal-DRbp0YnP.woff2 +0 -0
  101. tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-400-italic-BXrkWnoY.woff +0 -0
  102. tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-400-italic-Bhem1d5z.woff2 +0 -0
  103. tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-400-normal-DT8nEsYA.woff +0 -0
  104. tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-400-normal-OHaX69iP.woff2 +0 -0
  105. tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-500-normal-CcSTXKtO.woff2 +0 -0
  106. tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-500-normal-JgPl2bDS.woff +0 -0
  107. tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-700-normal-B004qtqu.woff2 +0 -0
  108. tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-700-normal-O6H_RRvN.woff +0 -0
  109. tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-400-italic-BwUYFJ2t.woff2 +0 -0
  110. tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-400-italic-DV8QogUk.woff +0 -0
  111. tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-400-normal-0o1laQ-g.woff2 +0 -0
  112. tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-400-normal-CPsdS8_S.woff +0 -0
  113. tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-500-normal-G9shSJ2z.woff +0 -0
  114. tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-500-normal-TFWhjk13.woff2 +0 -0
  115. tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-700-normal-BtNeb9D6.woff +0 -0
  116. tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-700-normal-D35V1G0s.woff2 +0 -0
  117. tangram_core/dist-frontend/assets/shader-BJmsOfPx.js +843 -0
  118. tangram_core/dist-frontend/assets/shader-BJmsOfPx.js.map +1 -0
  119. tangram_core/dist-frontend/assets/solid-polygon-layer-DiarVGxh.js +392 -0
  120. tangram_core/dist-frontend/assets/solid-polygon-layer-DiarVGxh.js.map +1 -0
  121. tangram_core/dist-frontend/assets/tesselator-49Dw9L5A.js +2 -0
  122. tangram_core/dist-frontend/assets/tesselator-49Dw9L5A.js.map +1 -0
  123. tangram_core/dist-frontend/assets/webgl-developer-tools-CZl8qVFg.js +7 -0
  124. tangram_core/dist-frontend/assets/webgl-developer-tools-CZl8qVFg.js.map +1 -0
  125. tangram_core/dist-frontend/assets/webgl-device-BY0-CUP6.js +3 -0
  126. tangram_core/dist-frontend/assets/webgl-device-BY0-CUP6.js.map +1 -0
  127. tangram_core/dist-frontend/assets/widget-BbOeHGj0.js +2 -0
  128. tangram_core/dist-frontend/assets/widget-BbOeHGj0.js.map +1 -0
  129. tangram_core/dist-frontend/core.js +60 -0
  130. tangram_core/dist-frontend/core.js.map +1 -0
  131. tangram_core/dist-frontend/extensions.js +609 -0
  132. tangram_core/dist-frontend/extensions.js.map +1 -0
  133. tangram_core/dist-frontend/favicon.ico +0 -0
  134. tangram_core/dist-frontend/favicon.png +0 -0
  135. tangram_core/dist-frontend/font-awesome.min.css +4 -0
  136. tangram_core/dist-frontend/fonts/FontAwesome.otf +0 -0
  137. tangram_core/dist-frontend/fonts/fontawesome-webfont.eot +0 -0
  138. tangram_core/dist-frontend/fonts/fontawesome-webfont.svg +2671 -0
  139. tangram_core/dist-frontend/fonts/fontawesome-webfont.ttf +0 -0
  140. tangram_core/dist-frontend/fonts/fontawesome-webfont.woff +0 -0
  141. tangram_core/dist-frontend/fonts/fontawesome-webfont.woff2 +0 -0
  142. tangram_core/dist-frontend/geo-layers.js +115 -0
  143. tangram_core/dist-frontend/geo-layers.js.map +1 -0
  144. tangram_core/dist-frontend/index.html +38 -0
  145. tangram_core/dist-frontend/json.js +3 -0
  146. tangram_core/dist-frontend/json.js.map +1 -0
  147. tangram_core/dist-frontend/layers.js +268 -0
  148. tangram_core/dist-frontend/layers.js.map +1 -0
  149. tangram_core/dist-frontend/lit-html.js +7 -0
  150. tangram_core/dist-frontend/lit-html.js.map +1 -0
  151. tangram_core/dist-frontend/mapbox.js +2 -0
  152. tangram_core/dist-frontend/mapbox.js.map +1 -0
  153. tangram_core/dist-frontend/maplibre-gl.js +59 -0
  154. tangram_core/dist-frontend/maplibre-gl.js.map +1 -0
  155. tangram_core/dist-frontend/mesh-layers.js +2 -0
  156. tangram_core/dist-frontend/mesh-layers.js.map +1 -0
  157. tangram_core/dist-frontend/rs1090_wasm.js +813 -0
  158. tangram_core/dist-frontend/rs1090_wasm_bg.wasm +0 -0
  159. tangram_core/dist-frontend/vue.esm-browser.prod.js +13 -0
  160. tangram_core/dist-frontend/widgets.js +3 -0
  161. tangram_core/dist-frontend/widgets.js.map +1 -0
  162. tangram_core/main.ts +16 -0
  163. tangram_core/plugin.py +70 -0
  164. tangram_core/plugin.ts +41 -0
  165. tangram_core/redis.py +89 -0
  166. tangram_core/user.css +114 -0
  167. tangram_core/vite-plugin-tangram.mjs +88 -0
  168. tangram_core-0.2.0.dist-info/METADATA +37 -0
  169. tangram_core-0.2.0.dist-info/RECORD +171 -0
  170. tangram_core-0.2.0.dist-info/WHEEL +4 -0
  171. tangram_core-0.2.0.dist-info/entry_points.txt +2 -0
tangram_core/App.vue ADDED
@@ -0,0 +1,335 @@
1
+ <template>
2
+ <!-- NOTE: copied largely from v0.1. -->
3
+ <div v-if="apiState === 'loading'" class="loading-container">
4
+ <div>initialising tangram…</div>
5
+ <div v-if="loadingMessage" class="loading-detail">{{ loadingMessage }}</div>
6
+ </div>
7
+ <div v-else-if="apiState === 'error'">
8
+ error: could not connect to the tangram backend.
9
+ </div>
10
+ <div v-else-if="apiState === 'ready' && tangramApi" class="main-container">
11
+ <div class="navbar" role="navigation">
12
+ <div class="navbar-brand">
13
+ <img src="/favicon.png" alt="tangram" class="navbar-logo" />
14
+ <span class="navbar-title">tangram</span>
15
+ </div>
16
+
17
+ <div class="navbar-spacer"></div>
18
+
19
+ <component
20
+ :is="widget.id"
21
+ v-for="widget in tangramApi.ui.widgets.TopBar"
22
+ :key="widget.id"
23
+ />
24
+ </div>
25
+
26
+ <div
27
+ id="sidebar"
28
+ class="sidebar"
29
+ :class="{
30
+ collapsed: !tangramApi.state.activeEntity || tangramApi.ui.isSidebarCollapsed
31
+ }"
32
+ >
33
+ <div class="sidebar-tabs">
34
+ <ul role="tablist">
35
+ <li>
36
+ <a role="tab" @click="tangramApi.ui.toggleSidebar()">
37
+ <span class="fa fa-plane"></span>
38
+ </a>
39
+ </li>
40
+ </ul>
41
+ </div>
42
+ <div class="sidebar-content">
43
+ <component
44
+ :is="widget.id"
45
+ v-for="widget in tangramApi.ui.widgets.SideBar"
46
+ :key="widget.id"
47
+ />
48
+ </div>
49
+ </div>
50
+
51
+ <div ref="mapContainer" class="map-container">
52
+ <component
53
+ :is="widget.id"
54
+ v-for="widget in tangramApi.ui.widgets.MapOverlay"
55
+ :key="widget.id"
56
+ />
57
+ </div>
58
+ </div>
59
+ </template>
60
+
61
+ <script setup lang="ts">
62
+ import { onMounted, onUnmounted, getCurrentInstance, ref, watch } from "vue";
63
+ import maplibregl from "maplibre-gl";
64
+ import { TangramApi } from "./api";
65
+ import { loadPlugins } from "./plugin";
66
+ import { layers, namedFlavor } from "@protomaps/basemaps";
67
+ import * as pmtiles from "pmtiles";
68
+
69
+ type ApiState = "loading" | "ready" | "error";
70
+
71
+ const app = getCurrentInstance()!.appContext.app;
72
+ const apiState = ref<ApiState>("loading");
73
+ const loadingMessage = ref<string>("");
74
+ const tangramApi = ref<TangramApi | null>(null);
75
+ const mapContainer = ref<HTMLElement | null>(null);
76
+ let mapInstance: maplibregl.Map | undefined = undefined;
77
+
78
+ onMounted(async () => {
79
+ try {
80
+ const api = await TangramApi.create(app);
81
+ app.provide("tangramApi", api);
82
+ await loadPlugins(api, progress => {
83
+ if (progress.stage === "manifest") {
84
+ loadingMessage.value = "fetching plugin manifest";
85
+ } else if (progress.stage === "plugin" && progress.pluginName) {
86
+ loadingMessage.value = `loading plugin: ${progress.pluginName}`;
87
+ } else if (progress.stage === "done") {
88
+ loadingMessage.value = "starting user interface";
89
+ }
90
+ });
91
+ tangramApi.value = api;
92
+ apiState.value = "ready";
93
+ } catch (e) {
94
+ console.error("failed to initialise tangram api:", e);
95
+ apiState.value = "error";
96
+ }
97
+ });
98
+
99
+ watch(
100
+ () => tangramApi.value?.state.activeEntity,
101
+ newEntity => {
102
+ if (newEntity) {
103
+ tangramApi.value?.ui.openSidebar();
104
+ }
105
+ }
106
+ );
107
+
108
+ watch(mapContainer, newEl => {
109
+ if (newEl && tangramApi.value && !mapInstance) {
110
+ const mapConfig = tangramApi.value.config.map;
111
+
112
+ const protocol = new pmtiles.Protocol();
113
+ maplibregl.addProtocol("pmtiles", protocol.tile);
114
+
115
+ if (typeof mapConfig.style === "object") {
116
+ // If the style is an object, we need to ensure it has the correct structure
117
+ // First we need to remove all None/null values from the style object
118
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
119
+ const removeNulls = (obj: any): any => {
120
+ if (Array.isArray(obj)) {
121
+ return obj.map(removeNulls);
122
+ }
123
+ if (typeof obj === "string") {
124
+ // This is a special case if we don't want to hardcode the root URL
125
+ // of the application in the style JSON, we can use #ROOT# as a placeholder
126
+ if (obj.includes("#ROOT#")) {
127
+ const rootUrl =
128
+ window.location.origin +
129
+ window.location.pathname.replace(/\/[^\/]*$/, "/");
130
+ return obj.replace(/#ROOT#/g, rootUrl);
131
+ }
132
+ return obj;
133
+ }
134
+ if (obj !== null && typeof obj === "object") {
135
+ return Object.entries(obj).reduce((acc, [key, value]) => {
136
+ if (value !== null && value !== undefined) {
137
+ acc[key] = removeNulls(value);
138
+ }
139
+ return acc;
140
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
+ }, {} as any);
142
+ }
143
+ return obj;
144
+ };
145
+
146
+ const styleObject = removeNulls(mapConfig.style);
147
+
148
+ if (!styleObject.layers) {
149
+ styleObject.layers = layers("protomaps", namedFlavor("light"), {
150
+ lang: mapConfig.lang
151
+ });
152
+ }
153
+ mapConfig.style = styleObject;
154
+ }
155
+
156
+ // @ts-expect-error TS2589: Type instantiation is excessively deep and possibly infinite
157
+ mapInstance = new maplibregl.Map({
158
+ container: newEl,
159
+ style: mapConfig.style,
160
+ center: [mapConfig.center_lon, mapConfig.center_lat],
161
+ zoom: mapConfig.zoom,
162
+ pitch: mapConfig.pitch,
163
+ bearing: mapConfig.bearing,
164
+ attributionControl: false
165
+ });
166
+
167
+ tangramApi.value.map.initialize(mapInstance);
168
+ }
169
+ });
170
+
171
+ onUnmounted(() => {
172
+ tangramApi.value?.map.dispose();
173
+ if (mapInstance) {
174
+ mapInstance.remove();
175
+ mapInstance = undefined;
176
+ }
177
+ });
178
+ </script>
179
+
180
+ <style>
181
+ #app {
182
+ height: 100%;
183
+ }
184
+
185
+ /* styles from v0.1 App.vue */
186
+ html {
187
+ width: 100%;
188
+ height: 100%;
189
+ }
190
+
191
+ body {
192
+ font-family: "B612", sans-serif;
193
+ width: 100%;
194
+ height: 100%;
195
+ margin: 0;
196
+ padding: 0;
197
+ }
198
+
199
+ .main-container {
200
+ height: 100%;
201
+ width: 100%;
202
+ display: flex;
203
+ flex-direction: column;
204
+ }
205
+
206
+ .navbar {
207
+ min-height: 50px;
208
+ background: white;
209
+ z-index: 500;
210
+ width: 100%;
211
+ box-sizing: border-box;
212
+ padding: 0.5rem 1rem;
213
+ display: flex;
214
+ flex-wrap: wrap;
215
+ align-items: center;
216
+ gap: 1.5rem;
217
+ border-bottom: 1px solid #e7e7e7;
218
+ }
219
+
220
+ .navbar-brand {
221
+ display: flex;
222
+ align-items: center;
223
+ }
224
+
225
+ .navbar-logo {
226
+ height: 24px;
227
+ margin-right: 5px;
228
+ }
229
+
230
+ .navbar-title {
231
+ padding-top: 0.3125rem;
232
+ padding-bottom: 0.3125rem;
233
+ font-size: 1.25rem;
234
+ margin-bottom: 0;
235
+ color: black;
236
+ }
237
+
238
+ .navbar-spacer {
239
+ margin-left: auto;
240
+ }
241
+
242
+ .sidebar {
243
+ position: absolute;
244
+ bottom: 15px;
245
+ left: 15px;
246
+ width: 21rem;
247
+ height: calc(100% - 95px);
248
+ transform: translate(2.5%, 0%);
249
+ overflow: hidden;
250
+ z-index: 1000;
251
+ box-shadow: 0 1px 5px rgba(0, 0, 0, 0.65);
252
+ border-radius: 4px;
253
+ display: flex;
254
+ }
255
+
256
+ .sidebar.collapsed {
257
+ width: 40px;
258
+ height: 40px;
259
+ }
260
+
261
+ .sidebar-tabs,
262
+ .sidebar-tabs > ul {
263
+ width: 40px;
264
+ margin: 0;
265
+ padding: 0;
266
+ list-style-type: none;
267
+ border-right: 1px solid #ddd;
268
+ }
269
+
270
+ .sidebar-tabs > li,
271
+ .sidebar-tabs > ul > li {
272
+ width: 40px;
273
+ height: 40px;
274
+ color: #333;
275
+ font-size: 12pt;
276
+ font-family: "B612", monospace;
277
+ overflow: hidden;
278
+ transition: all 80ms;
279
+ }
280
+
281
+ .sidebar-tabs > li > a,
282
+ .sidebar-tabs > ul > li > a {
283
+ display: block;
284
+ width: 40px;
285
+ height: 100%;
286
+ line-height: 40px;
287
+ color: inherit;
288
+ text-decoration: none;
289
+ text-align: center;
290
+ cursor: pointer;
291
+ }
292
+
293
+ .sidebar-content {
294
+ flex: 1;
295
+ background-color: rgba(255, 255, 255, 1);
296
+ overflow-x: hidden;
297
+ overflow-y: auto;
298
+ padding: 5px;
299
+ display: flex;
300
+ flex-direction: column;
301
+ }
302
+
303
+ .sidebar.collapsed > .sidebar-content {
304
+ overflow-y: hidden;
305
+ }
306
+
307
+ .control-container {
308
+ position: absolute;
309
+ right: 55px;
310
+ bottom: 90px;
311
+ }
312
+
313
+ .map-container {
314
+ flex: 1;
315
+ flex-grow: 1;
316
+ }
317
+
318
+ .control-attribution {
319
+ display: none !important;
320
+ }
321
+
322
+ .top,
323
+ .bottom {
324
+ z-index: 400;
325
+ }
326
+
327
+ .loading-container {
328
+ padding: 1.5rem;
329
+ }
330
+
331
+ .loading-detail {
332
+ margin-top: 0.5rem;
333
+ color: #555;
334
+ }
335
+ </style>
@@ -0,0 +1,5 @@
1
+ from .backend import BackendState, InjectBackendState
2
+ from .config import Config
3
+ from .plugin import Plugin
4
+
5
+ __all__ = ["BackendState", "Config", "InjectBackendState", "Plugin"]
@@ -0,0 +1,141 @@
1
+ import asyncio
2
+ import logging
3
+ import os
4
+ from importlib.abc import Traversable
5
+ from pathlib import Path
6
+ from typing import Annotated
7
+
8
+ import platformdirs
9
+ import typer
10
+ from rich.console import Console
11
+ from rich.table import Table
12
+
13
+ from .backend import get_log_config_dict, resolve_frontend, start_tasks
14
+ from .config import Config
15
+ from .plugin import load_plugin, scan_plugins
16
+
17
+ app = typer.Typer(no_args_is_help=True, pretty_exceptions_enable=False)
18
+ logger = logging.getLogger(__name__)
19
+ stderr = Console(stderr=True)
20
+
21
+
22
+ def default_config_file() -> Path:
23
+ if (xdg_config := os.environ.get("XDG_CONFIG_HOME")) is not None:
24
+ config_dir = Path(xdg_config) / "tangram"
25
+ else:
26
+ config_dir = Path(platformdirs.user_config_dir(appname="tangram"))
27
+ if not config_dir.exists():
28
+ config_dir.mkdir(parents=True, exist_ok=True)
29
+
30
+ return Path(config_dir) / "tangram.toml"
31
+
32
+
33
+ @app.command()
34
+ def serve(
35
+ config: Annotated[
36
+ Path,
37
+ typer.Option(
38
+ help="Path to the tangram.toml config file.",
39
+ envvar="TANGRAM_CONFIG",
40
+ default_factory=default_config_file,
41
+ ),
42
+ ],
43
+ ) -> None:
44
+ """Serves the core tangram frontend and backend services."""
45
+ if not config.is_file():
46
+ logger.error(f"config file not found: {config}")
47
+ raise typer.Exit()
48
+
49
+ cfg = Config.from_file(config)
50
+ logging.config.dictConfig(get_log_config_dict(cfg))
51
+
52
+ try:
53
+ asyncio.run(start_tasks(cfg))
54
+ except KeyboardInterrupt:
55
+ logger.info("shutting down services.")
56
+
57
+
58
+ @app.command(name="list-plugins")
59
+ def list_plugins(
60
+ config_path: Annotated[
61
+ Path | None,
62
+ typer.Option("--config", help="Path to the tangram.toml config file."),
63
+ ] = None,
64
+ all_plugins: Annotated[
65
+ bool, typer.Option("--all", help="Load all discovered plugins.")
66
+ ] = False,
67
+ ) -> None:
68
+ """Lists discovered plugins and their components."""
69
+ table = Table()
70
+ table.add_column("plugin")
71
+ table.add_column("status")
72
+ table.add_column("frontend")
73
+ table.add_column("routers")
74
+ table.add_column("services")
75
+
76
+ enabled_plugins: list[str] | None = None
77
+ if config_path and config_path.is_file():
78
+ config = Config.from_file(config_path)
79
+ enabled_plugins = config.core.plugins
80
+
81
+ for entry_point in scan_plugins():
82
+ plugin_name = entry_point.name
83
+ load_this_plugin = all_plugins or (
84
+ enabled_plugins is not None and plugin_name in enabled_plugins
85
+ )
86
+
87
+ if not load_this_plugin:
88
+ table.add_row(plugin_name, "available", "?", "?", "?")
89
+ continue
90
+
91
+ if (p := load_plugin(entry_point)) is None:
92
+ status_str = "[red]load failed[/red]"
93
+ table.add_row(plugin_name, status_str, "!", "!", "!")
94
+ continue
95
+ name, plugin = p
96
+ status = (
97
+ "enabled"
98
+ if enabled_plugins and plugin_name in enabled_plugins
99
+ else "loaded"
100
+ )
101
+ frontend_str = ""
102
+ if plugin_path := plugin.frontend_path:
103
+ if resolved_path := resolve_frontend(path=name, dist_name=plugin_name):
104
+ size_kb = get_path_size(resolved_path) / 1024
105
+ frontend_str = f"{size_kb:.1f} B"
106
+ else:
107
+ frontend_str = f"[yellow]({plugin_path} not found)[/yellow]"
108
+
109
+ status_str = f"[green]{status}[/green]"
110
+ router_prefixes = [func.prefix for func in plugin.routers]
111
+ service_names = [func.__name__ for _, func in plugin.services]
112
+
113
+ table.add_row(
114
+ plugin_name,
115
+ status_str,
116
+ frontend_str,
117
+ "\n".join(router_prefixes),
118
+ "\n".join(service_names),
119
+ )
120
+
121
+ stderr.print(table)
122
+
123
+
124
+ def get_path_size(path: Path | Traversable) -> int:
125
+ total_size = 0
126
+ for item in path.iterdir():
127
+ if item.is_file():
128
+ with item.open("rb") as f:
129
+ total_size += len(f.read())
130
+ elif item.is_dir():
131
+ total_size += get_path_size(item)
132
+ return total_size
133
+
134
+
135
+ @app.command()
136
+ def develop() -> None:
137
+ raise SystemExit
138
+
139
+
140
+ if __name__ == "__main__":
141
+ app()
Binary file
tangram_core/_core.pyi ADDED
@@ -0,0 +1,38 @@
1
+ # This file is automatically generated by pyo3_stub_gen
2
+ # ruff: noqa: E501, F401
3
+
4
+ import builtins
5
+ import typing
6
+
7
+ @typing.final
8
+ class ChannelConfig:
9
+ @property
10
+ def host(self) -> builtins.str: ...
11
+ @host.setter
12
+ def host(self, value: builtins.str) -> None: ...
13
+ @property
14
+ def port(self) -> builtins.int: ...
15
+ @port.setter
16
+ def port(self, value: builtins.int) -> None: ...
17
+ @property
18
+ def redis_url(self) -> builtins.str: ...
19
+ @redis_url.setter
20
+ def redis_url(self, value: builtins.str) -> None: ...
21
+ @property
22
+ def jwt_secret(self) -> builtins.str: ...
23
+ @jwt_secret.setter
24
+ def jwt_secret(self, value: builtins.str) -> None: ...
25
+ @property
26
+ def jwt_expiration_secs(self) -> builtins.int: ...
27
+ @jwt_expiration_secs.setter
28
+ def jwt_expiration_secs(self, value: builtins.int) -> None: ...
29
+ @property
30
+ def id_length(self) -> builtins.int: ...
31
+ @id_length.setter
32
+ def id_length(self, value: builtins.int) -> None: ...
33
+ def __new__(cls, host: builtins.str, port: builtins.int, redis_url: builtins.str, jwt_secret: builtins.str, jwt_expiration_secs: builtins.int, id_length: builtins.int) -> ChannelConfig: ...
34
+
35
+ def init_tracing_stderr(filter_str: builtins.str) -> None: ...
36
+
37
+ def run(config: ChannelConfig) -> typing.Any: ...
38
+