tangram-core 0.3.0__cp310-cp310-manylinux_2_28_aarch64.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.
- tangram_core/App.vue +441 -0
- tangram_core/CommandPalette.vue +200 -0
- tangram_core/HighlightText.vue +32 -0
- tangram_core/__Timeline.vue +300 -0
- tangram_core/__init__.py +5 -0
- tangram_core/__main__.py +331 -0
- tangram_core/_core.cpython-310-aarch64-linux-gnu.so +0 -0
- tangram_core/_core.pyi +38 -0
- tangram_core/api.ts +652 -0
- tangram_core/backend.py +458 -0
- tangram_core/components.ts +2 -0
- tangram_core/config.py +167 -0
- tangram_core/dist-frontend/aggregation-layers.js +521 -0
- tangram_core/dist-frontend/aggregation-layers.js.map +1 -0
- tangram_core/dist-frontend/assets/_commonjsHelpers-CqkleIqs.js +2 -0
- tangram_core/dist-frontend/assets/_commonjsHelpers-CqkleIqs.js.map +1 -0
- tangram_core/dist-frontend/assets/array-utils-flat-BBMak426.js +11 -0
- tangram_core/dist-frontend/assets/array-utils-flat-BBMak426.js.map +1 -0
- tangram_core/dist-frontend/assets/assert-cyW4mg7q.js +3 -0
- tangram_core/dist-frontend/assets/assert-cyW4mg7q.js.map +1 -0
- tangram_core/dist-frontend/assets/b612-latin-400-italic-DePNXA0a.woff +0 -0
- tangram_core/dist-frontend/assets/b612-latin-400-italic-a-4GLPtl.woff2 +0 -0
- tangram_core/dist-frontend/assets/b612-latin-400-normal-CC98FVm_.woff2 +0 -0
- tangram_core/dist-frontend/assets/b612-latin-400-normal-JbZ7xwUX.woff +0 -0
- tangram_core/dist-frontend/assets/b612-latin-700-normal-B_Snq1wd.woff +0 -0
- tangram_core/dist-frontend/assets/b612-latin-700-normal-BinQrnoB.woff2 +0 -0
- tangram_core/dist-frontend/assets/clip-extension-D-rbmFPj.js +26 -0
- tangram_core/dist-frontend/assets/clip-extension-D-rbmFPj.js.map +1 -0
- tangram_core/dist-frontend/assets/color-CUNNsFV-.js +17 -0
- tangram_core/dist-frontend/assets/color-CUNNsFV-.js.map +1 -0
- tangram_core/dist-frontend/assets/cube-geometry-v0HQ793i.js +2 -0
- tangram_core/dist-frontend/assets/cube-geometry-v0HQ793i.js.map +1 -0
- tangram_core/dist-frontend/assets/deep-equal-BTW2ZN6S.js +2 -0
- tangram_core/dist-frontend/assets/deep-equal-BTW2ZN6S.js.map +1 -0
- tangram_core/dist-frontend/assets/fly-to-interpolator-CIXGjOdo.js +2 -0
- tangram_core/dist-frontend/assets/fly-to-interpolator-CIXGjOdo.js.map +1 -0
- tangram_core/dist-frontend/assets/geojson-layer-DgMOQ4Qu.js +1010 -0
- tangram_core/dist-frontend/assets/geojson-layer-DgMOQ4Qu.js.map +1 -0
- tangram_core/dist-frontend/assets/globe-view-Day_n1iB.js +94 -0
- tangram_core/dist-frontend/assets/globe-view-Day_n1iB.js.map +1 -0
- tangram_core/dist-frontend/assets/globe-viewport-tqhQW7C4.js +2 -0
- tangram_core/dist-frontend/assets/globe-viewport-tqhQW7C4.js.map +1 -0
- tangram_core/dist-frontend/assets/image-loader-hHJsndO6.js +2 -0
- tangram_core/dist-frontend/assets/image-loader-hHJsndO6.js.map +1 -0
- tangram_core/dist-frontend/assets/inconsolata-latin-400-normal-DTZQ6lD6.woff2 +0 -0
- tangram_core/dist-frontend/assets/inconsolata-latin-400-normal-HYADljCo.woff +0 -0
- tangram_core/dist-frontend/assets/inconsolata-latin-700-normal-ByjKuJjN.woff2 +0 -0
- tangram_core/dist-frontend/assets/inconsolata-latin-700-normal-DzgUY3Rl.woff +0 -0
- tangram_core/dist-frontend/assets/inconsolata-latin-ext-400-normal-BaHVOdFB.woff2 +0 -0
- tangram_core/dist-frontend/assets/inconsolata-latin-ext-400-normal-yvPjCxxx.woff +0 -0
- tangram_core/dist-frontend/assets/inconsolata-latin-ext-700-normal-D0Kpgs_9.woff2 +0 -0
- tangram_core/dist-frontend/assets/inconsolata-latin-ext-700-normal-Dlt-daqV.woff +0 -0
- tangram_core/dist-frontend/assets/inconsolata-vietnamese-400-normal-ByiM2lek.woff +0 -0
- tangram_core/dist-frontend/assets/inconsolata-vietnamese-400-normal-DfC_iMic.woff2 +0 -0
- tangram_core/dist-frontend/assets/inconsolata-vietnamese-700-normal-DLCFFAUf.woff +0 -0
- tangram_core/dist-frontend/assets/inconsolata-vietnamese-700-normal-DuasYmn8.woff2 +0 -0
- tangram_core/dist-frontend/assets/index-CcogpxdD.js +824 -0
- tangram_core/dist-frontend/assets/index-CcogpxdD.js.map +1 -0
- tangram_core/dist-frontend/assets/index-SSLdizTv.css +1 -0
- tangram_core/dist-frontend/assets/layer-DPcO4AXQ.js +555 -0
- tangram_core/dist-frontend/assets/layer-DPcO4AXQ.js.map +1 -0
- tangram_core/dist-frontend/assets/layer-extension-CYwTXf73.js +2 -0
- tangram_core/dist-frontend/assets/layer-extension-CYwTXf73.js.map +1 -0
- tangram_core/dist-frontend/assets/mesh-layers-wiqredoy.js +1123 -0
- tangram_core/dist-frontend/assets/mesh-layers-wiqredoy.js.map +1 -0
- tangram_core/dist-frontend/assets/orthographic-viewport-B4nCj5tn.js +2 -0
- tangram_core/dist-frontend/assets/orthographic-viewport-B4nCj5tn.js.map +1 -0
- tangram_core/dist-frontend/assets/pick-layers-pass-C-3k0wbN.js +2 -0
- tangram_core/dist-frontend/assets/pick-layers-pass-C-3k0wbN.js.map +1 -0
- tangram_core/dist-frontend/assets/project-BTjD2Imj.js +760 -0
- tangram_core/dist-frontend/assets/project-BTjD2Imj.js.map +1 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-400-italic-4qS3_zkX.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-400-italic-CDK-EZBY.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-400-normal-Bgns473E.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-400-normal-_T2aQlWs.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-500-normal-CvEVpWxD.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-500-normal-s4PklZE0.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-700-normal-9RN-Z7cI.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-700-normal-BGMkBBYx.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-400-italic-C7erd-g8.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-400-italic-DR5R5TWx.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-400-normal-DGo1Ayjq.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-400-normal-WtM1l1qc.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-500-normal-C8FNIdXm.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-500-normal-TLDmfi3Q.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-700-normal-CTXjXnze.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-cyrillic-ext-700-normal-CWPRiRXS.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-greek-400-italic-CR6qj4Z4.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-greek-400-italic-DHRaIs10.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-greek-400-normal-D5vBSIyg.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-greek-400-normal-FabMgVmk.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-greek-500-normal-BIN62cw9.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-greek-500-normal-Hsn-wDIp.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-greek-700-normal-89Up2Xly.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-greek-700-normal-DWMOA2VK.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-400-italic-D_BR-3LG.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-400-italic-om57GXsO.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-400-normal-BICmKrXV.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-400-normal-D2e7XwB1.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-500-normal-3p2daRJW.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-500-normal-Dc9bsamC.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-700-normal-BOl6B_hI.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-700-normal-DRbp0YnP.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-400-italic-BXrkWnoY.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-400-italic-Bhem1d5z.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-400-normal-DT8nEsYA.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-400-normal-OHaX69iP.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-500-normal-CcSTXKtO.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-500-normal-JgPl2bDS.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-700-normal-B004qtqu.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-latin-ext-700-normal-O6H_RRvN.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-400-italic-BwUYFJ2t.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-400-italic-DV8QogUk.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-400-normal-0o1laQ-g.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-400-normal-CPsdS8_S.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-500-normal-G9shSJ2z.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-500-normal-TFWhjk13.woff2 +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-700-normal-BtNeb9D6.woff +0 -0
- tangram_core/dist-frontend/assets/roboto-condensed-vietnamese-700-normal-D35V1G0s.woff2 +0 -0
- tangram_core/dist-frontend/assets/shader-Cbdysp2j.js +843 -0
- tangram_core/dist-frontend/assets/shader-Cbdysp2j.js.map +1 -0
- tangram_core/dist-frontend/assets/solid-polygon-layer-DJFl_7Ca.js +392 -0
- tangram_core/dist-frontend/assets/solid-polygon-layer-DJFl_7Ca.js.map +1 -0
- tangram_core/dist-frontend/assets/tesselator-CENyUZ2p.js +2 -0
- tangram_core/dist-frontend/assets/tesselator-CENyUZ2p.js.map +1 -0
- tangram_core/dist-frontend/assets/webgl-developer-tools-utTNOsNf.js +7 -0
- tangram_core/dist-frontend/assets/webgl-developer-tools-utTNOsNf.js.map +1 -0
- tangram_core/dist-frontend/assets/webgl-device-BYRB-GQX.js +3 -0
- tangram_core/dist-frontend/assets/webgl-device-BYRB-GQX.js.map +1 -0
- tangram_core/dist-frontend/assets/widget-BjgEeHAL.js +2 -0
- tangram_core/dist-frontend/assets/widget-BjgEeHAL.js.map +1 -0
- tangram_core/dist-frontend/core.js +60 -0
- tangram_core/dist-frontend/core.js.map +1 -0
- tangram_core/dist-frontend/extensions.js +609 -0
- tangram_core/dist-frontend/extensions.js.map +1 -0
- tangram_core/dist-frontend/favicon.ico +0 -0
- tangram_core/dist-frontend/favicon.png +0 -0
- tangram_core/dist-frontend/geo-layers.js +115 -0
- tangram_core/dist-frontend/geo-layers.js.map +1 -0
- tangram_core/dist-frontend/index.html +39 -0
- tangram_core/dist-frontend/json.js +3 -0
- tangram_core/dist-frontend/json.js.map +1 -0
- tangram_core/dist-frontend/layers.js +268 -0
- tangram_core/dist-frontend/layers.js.map +1 -0
- tangram_core/dist-frontend/mapbox.js +2 -0
- tangram_core/dist-frontend/mapbox.js.map +1 -0
- tangram_core/dist-frontend/mesh-layers.js +2 -0
- tangram_core/dist-frontend/mesh-layers.js.map +1 -0
- tangram_core/dist-frontend/widgets.js +3 -0
- tangram_core/dist-frontend/widgets.js.map +1 -0
- tangram_core/main.ts +28 -0
- tangram_core/package.json +62 -0
- tangram_core/plugin.py +109 -0
- tangram_core/plugin.ts +47 -0
- tangram_core/redis.py +89 -0
- tangram_core/user.css +114 -0
- tangram_core/utils.ts +143 -0
- tangram_core/vite-plugin-tangram.mjs +155 -0
- tangram_core-0.3.0.dist-info/METADATA +101 -0
- tangram_core-0.3.0.dist-info/RECORD +162 -0
- tangram_core-0.3.0.dist-info/WHEEL +4 -0
- tangram_core-0.3.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
<!-- NOTE: This component is a relic of tangram v0.1 and is kept for reference.
|
|
2
|
+
It is scheduled for removal in v0.4 when playback is properly implemented
|
|
3
|
+
in the backend. -->
|
|
4
|
+
<template>
|
|
5
|
+
<div class="timeline-container" :style="styles">
|
|
6
|
+
<div
|
|
7
|
+
class="timeline-flex"
|
|
8
|
+
:class="{ 'flex-row': direction !== 'col', 'flex-col': direction === 'col' }"
|
|
9
|
+
>
|
|
10
|
+
<div
|
|
11
|
+
:class="{
|
|
12
|
+
'progress-row': direction !== 'col',
|
|
13
|
+
'progress-col': direction === 'col'
|
|
14
|
+
}"
|
|
15
|
+
>
|
|
16
|
+
<VueSlider
|
|
17
|
+
:min="dateArray[0].startOf('day').unix()"
|
|
18
|
+
:max="dateArray[dateArray.length - 1].endOf('day').unix()"
|
|
19
|
+
:tooltip-style="{ display: 'none' }"
|
|
20
|
+
:dot-style="{ display: 'none' }"
|
|
21
|
+
:process-style="{ background: '#0000bb50', borderRadius: '1px' }"
|
|
22
|
+
v-model="curTime"
|
|
23
|
+
:height="8"
|
|
24
|
+
:dot-size="8"
|
|
25
|
+
style="padding: 0"
|
|
26
|
+
@change="onChangeTime"
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
<div
|
|
30
|
+
v-if="curTime"
|
|
31
|
+
:style="{ ...getTooltip, 'z-index': styles.zIndex + 1 || '401' }"
|
|
32
|
+
:class="{
|
|
33
|
+
'tooltip-row': direction !== 'col',
|
|
34
|
+
'tooltip-col': direction === 'col'
|
|
35
|
+
}"
|
|
36
|
+
>
|
|
37
|
+
{{ getTooltipText() }}
|
|
38
|
+
</div>
|
|
39
|
+
<div
|
|
40
|
+
v-for="(item, index) in dateArray"
|
|
41
|
+
:key="index"
|
|
42
|
+
:style="{ width: 100 / dateArray.length + '%' }"
|
|
43
|
+
class="date-block"
|
|
44
|
+
>
|
|
45
|
+
<div style="width: 100%; display: flex">
|
|
46
|
+
<div
|
|
47
|
+
v-for="num in ticks"
|
|
48
|
+
:key="num"
|
|
49
|
+
:style="{ width: 100 / ticks.length + '%' }"
|
|
50
|
+
:class="{
|
|
51
|
+
'ticks-row': direction !== 'col',
|
|
52
|
+
'ticks-col': direction === 'col'
|
|
53
|
+
}"
|
|
54
|
+
>
|
|
55
|
+
<span
|
|
56
|
+
v-if="num !== '00' && showTick"
|
|
57
|
+
class="tick-num"
|
|
58
|
+
:class="{
|
|
59
|
+
'tick-num-row': direction !== 'col',
|
|
60
|
+
'tick-num-col': direction === 'col'
|
|
61
|
+
}"
|
|
62
|
+
>{{ num }}</span
|
|
63
|
+
>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
<div v-if="showTime" class="date-content">{{ item.format(dateFormat) }}</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</template>
|
|
71
|
+
|
|
72
|
+
<script>
|
|
73
|
+
import dayjs from "dayjs";
|
|
74
|
+
import VueSlider from "vue-slider-component";
|
|
75
|
+
import "vue-slider-component/theme/antd.css";
|
|
76
|
+
export default {
|
|
77
|
+
components: {
|
|
78
|
+
VueSlider
|
|
79
|
+
},
|
|
80
|
+
props: {
|
|
81
|
+
progressColor: {
|
|
82
|
+
type: String,
|
|
83
|
+
default: "#0000bb50"
|
|
84
|
+
},
|
|
85
|
+
styles: {
|
|
86
|
+
type: Object,
|
|
87
|
+
default: {}
|
|
88
|
+
},
|
|
89
|
+
unitNum: {
|
|
90
|
+
type: Number,
|
|
91
|
+
default: 8
|
|
92
|
+
},
|
|
93
|
+
dateFormat: {
|
|
94
|
+
type: String,
|
|
95
|
+
default: "MM-DD"
|
|
96
|
+
},
|
|
97
|
+
showToolTip: {
|
|
98
|
+
type: Boolean,
|
|
99
|
+
default: true
|
|
100
|
+
},
|
|
101
|
+
showTime: {
|
|
102
|
+
type: Boolean,
|
|
103
|
+
default: true
|
|
104
|
+
},
|
|
105
|
+
showTick: {
|
|
106
|
+
type: Boolean,
|
|
107
|
+
default: true
|
|
108
|
+
},
|
|
109
|
+
draggable: {
|
|
110
|
+
type: Boolean,
|
|
111
|
+
default: true
|
|
112
|
+
},
|
|
113
|
+
dateArray: {
|
|
114
|
+
type: Array,
|
|
115
|
+
default: () => [
|
|
116
|
+
dayjs().subtract(3, "day"),
|
|
117
|
+
dayjs().subtract(2, "day"),
|
|
118
|
+
dayjs().subtract(1, "day"),
|
|
119
|
+
dayjs(),
|
|
120
|
+
dayjs().add(1, "day"),
|
|
121
|
+
dayjs().add(2, "day"),
|
|
122
|
+
dayjs().add(3, "day")
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
currentTime: {
|
|
126
|
+
type: Object,
|
|
127
|
+
default: () => dayjs()
|
|
128
|
+
},
|
|
129
|
+
direction: {
|
|
130
|
+
type: String,
|
|
131
|
+
default: "row"
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
data() {
|
|
135
|
+
return {
|
|
136
|
+
curTime: this.currentTime.unix()
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
computed: {
|
|
140
|
+
getTooltip() {
|
|
141
|
+
const cur = this.curTime;
|
|
142
|
+
const start = this.dateArray[0].startOf("day").unix();
|
|
143
|
+
const end = this.dateArray[this.dateArray.length - 1].endOf("day").unix();
|
|
144
|
+
if (this.direction !== "col") {
|
|
145
|
+
return { left: ((cur - start) / (end - start)) * 100 + "%" };
|
|
146
|
+
} else {
|
|
147
|
+
return { top: ((cur - start) / (end - start)) * 100 + "%" };
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
ticks() {
|
|
151
|
+
const arr = [];
|
|
152
|
+
for (let i = 0; i < this.unitNum; i++) {
|
|
153
|
+
const time = 24 / this.unitNum;
|
|
154
|
+
const timeTxt = i * time < 10 ? "0" + i * time : i * time;
|
|
155
|
+
arr.push(timeTxt);
|
|
156
|
+
}
|
|
157
|
+
return arr;
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
methods: {
|
|
161
|
+
getTooltipText() {
|
|
162
|
+
return dayjs.unix(this.curTime).format("HH:mm");
|
|
163
|
+
},
|
|
164
|
+
onChangeTime(e) {
|
|
165
|
+
const num = e / 100;
|
|
166
|
+
const start = this.dateArray[0].startOf("day").unix();
|
|
167
|
+
const end = this.dateArray[this.dateArray.length - 1].endOf("day").unix();
|
|
168
|
+
const now = (end - start) * e;
|
|
169
|
+
},
|
|
170
|
+
handleDragStart(e) {
|
|
171
|
+
console.log(e);
|
|
172
|
+
e.preventDefault();
|
|
173
|
+
e.stopPropagation();
|
|
174
|
+
},
|
|
175
|
+
handleDrop(e) {
|
|
176
|
+
console.log(e);
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
e.stopPropagation();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
</script>
|
|
183
|
+
<style>
|
|
184
|
+
.timeline-container {
|
|
185
|
+
z-index: 100;
|
|
186
|
+
cursor: default;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.timeline-container .timeline-flex {
|
|
190
|
+
display: flex;
|
|
191
|
+
position: relative;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.timeline-container .flex-row {
|
|
195
|
+
flex-direction: row;
|
|
196
|
+
width: 100%;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.timeline-container .flex-col {
|
|
200
|
+
flex-direction: column;
|
|
201
|
+
height: 100%;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.timeline-container .date-block {
|
|
205
|
+
text-align: center;
|
|
206
|
+
border-right: 1px solid #808080;
|
|
207
|
+
border-top: 1px solid #808080;
|
|
208
|
+
z-index: 1;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.timeline-container .date-block:first-child {
|
|
212
|
+
border-left: none;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.timeline-container .date-block .date-content {
|
|
216
|
+
padding-top: 10px;
|
|
217
|
+
padding-bottom: 5px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.timeline-container .ticks-row {
|
|
221
|
+
border-left: 1px solid #808080;
|
|
222
|
+
font-size: 8px;
|
|
223
|
+
height: 8px;
|
|
224
|
+
width: 8px;
|
|
225
|
+
position: relative;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.timeline-container .date-block .ticks-row:first-child {
|
|
229
|
+
border-left: none;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.timeline-container .tick-num {
|
|
233
|
+
position: absolute;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.timeline-container .tick-num-row {
|
|
237
|
+
left: 0;
|
|
238
|
+
transform: translateX(-50%);
|
|
239
|
+
top: 10px;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.timeline-container .tick-num-col {
|
|
243
|
+
top: 0;
|
|
244
|
+
transform: translateY(-50%);
|
|
245
|
+
left: 5px;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.timeline-container .progress-row {
|
|
249
|
+
height: 10px;
|
|
250
|
+
position: absolute;
|
|
251
|
+
top: 0;
|
|
252
|
+
width: 100%;
|
|
253
|
+
left: 0;
|
|
254
|
+
background: #00000030;
|
|
255
|
+
cursor: pointer;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.timeline-container .progress-col {
|
|
259
|
+
width: 10px;
|
|
260
|
+
position: absolute;
|
|
261
|
+
height: 100%;
|
|
262
|
+
top: 0;
|
|
263
|
+
left: 0;
|
|
264
|
+
background: #00000030;
|
|
265
|
+
cursor: pointer;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.timeline-container .tooltip-row {
|
|
269
|
+
position: absolute;
|
|
270
|
+
border-radius: 5px;
|
|
271
|
+
width: 50px;
|
|
272
|
+
height: 30px;
|
|
273
|
+
border: 1px solid #e0e0e0;
|
|
274
|
+
display: flex;
|
|
275
|
+
align-items: center;
|
|
276
|
+
justify-content: center;
|
|
277
|
+
transform: translateX(-50%);
|
|
278
|
+
top: -40px;
|
|
279
|
+
background: #0000bb60;
|
|
280
|
+
color: white;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.timeline-container .tooltip-row:after {
|
|
284
|
+
top: 100%;
|
|
285
|
+
left: 20px;
|
|
286
|
+
border: solid transparent;
|
|
287
|
+
content: " ";
|
|
288
|
+
height: 0;
|
|
289
|
+
width: 0;
|
|
290
|
+
position: absolute;
|
|
291
|
+
border-top-color: #0000bb60;
|
|
292
|
+
border-width: 5px;
|
|
293
|
+
margin-left: -5px;
|
|
294
|
+
-moz-user-select: none;
|
|
295
|
+
-webkit-user-select: none;
|
|
296
|
+
-ms-user-select: none;
|
|
297
|
+
-khtml-user-select: none;
|
|
298
|
+
user-select: none;
|
|
299
|
+
}
|
|
300
|
+
</style>
|
tangram_core/__init__.py
ADDED
tangram_core/__main__.py
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
#!/usr/bin/env -S uv run --script
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.12"
|
|
4
|
+
# dependencies = ["typer"]
|
|
5
|
+
# ///
|
|
6
|
+
# NOTE: put all non-standard-library imports (including `tangram_core`) inside
|
|
7
|
+
# functions.
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import logging
|
|
11
|
+
import logging.config
|
|
12
|
+
import re
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Annotated, Any, Generator, TypeAlias
|
|
17
|
+
|
|
18
|
+
if sys.version_info >= (3, 11):
|
|
19
|
+
from importlib.resources.abc import Traversable
|
|
20
|
+
else:
|
|
21
|
+
from importlib.abc import Traversable
|
|
22
|
+
|
|
23
|
+
import typer
|
|
24
|
+
from rich.console import Console
|
|
25
|
+
|
|
26
|
+
from .config import default_config_file
|
|
27
|
+
|
|
28
|
+
app = typer.Typer(no_args_is_help=True, pretty_exceptions_enable=False)
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
stderr = Console(stderr=True)
|
|
31
|
+
stdout = Console(stderr=False)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def print_error(v: Any) -> None:
|
|
35
|
+
stdout.print(f"[bold red]error[/bold red]: {v}")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def print_success(v: Any) -> None:
|
|
39
|
+
stdout.print(f"[bold green]success[/bold green]: {v}")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
PathTangramConfig: TypeAlias = Annotated[
|
|
43
|
+
Path,
|
|
44
|
+
typer.Option(
|
|
45
|
+
help="Path to the tangram.toml config file.",
|
|
46
|
+
envvar="TANGRAM_CONFIG",
|
|
47
|
+
default_factory=default_config_file,
|
|
48
|
+
exists=True,
|
|
49
|
+
dir_okay=False,
|
|
50
|
+
),
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@app.command()
|
|
55
|
+
def serve(
|
|
56
|
+
config: PathTangramConfig,
|
|
57
|
+
) -> None:
|
|
58
|
+
"""Serves the core tangram frontend and backend services."""
|
|
59
|
+
from .backend import Runtime, get_log_config_dict
|
|
60
|
+
from .config import Config
|
|
61
|
+
|
|
62
|
+
cfg = Config.from_file(config)
|
|
63
|
+
logging.config.dictConfig(get_log_config_dict(cfg))
|
|
64
|
+
|
|
65
|
+
async def run_server() -> None:
|
|
66
|
+
async with Runtime(cfg) as runtime:
|
|
67
|
+
await runtime.wait()
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
asyncio.run(run_server())
|
|
71
|
+
except KeyboardInterrupt:
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@app.command()
|
|
76
|
+
def list_plugins(
|
|
77
|
+
config_path: PathTangramConfig | None = None,
|
|
78
|
+
load_all: Annotated[
|
|
79
|
+
bool, typer.Option("--all", "-a", help="Load all discovered plugins.")
|
|
80
|
+
] = False,
|
|
81
|
+
) -> None:
|
|
82
|
+
"""Lists discovered plugins and their components."""
|
|
83
|
+
from rich.table import Table
|
|
84
|
+
|
|
85
|
+
from .backend import resolve_frontend
|
|
86
|
+
from .config import Config
|
|
87
|
+
from .plugin import load_plugin, scan_plugins
|
|
88
|
+
|
|
89
|
+
table = Table()
|
|
90
|
+
table.add_column("plugin")
|
|
91
|
+
table.add_column("status")
|
|
92
|
+
table.add_column("frontend")
|
|
93
|
+
table.add_column("routers")
|
|
94
|
+
table.add_column("services")
|
|
95
|
+
|
|
96
|
+
enabled_plugins: list[str] | None = None
|
|
97
|
+
if config_path and config_path.is_file():
|
|
98
|
+
config = Config.from_file(config_path)
|
|
99
|
+
enabled_plugins = config.core.plugins
|
|
100
|
+
|
|
101
|
+
for entry_point in scan_plugins():
|
|
102
|
+
plugin_name = entry_point.name
|
|
103
|
+
version = dist.version if (dist := entry_point.dist) is not None else "?"
|
|
104
|
+
plugin_str = f"{plugin_name}=={version}"
|
|
105
|
+
load_this_plugin = load_all or (
|
|
106
|
+
enabled_plugins is not None and plugin_name in enabled_plugins
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
if not load_this_plugin:
|
|
110
|
+
table.add_row(plugin_str, "[cyan]available[/cyan]", "?", "?", "?")
|
|
111
|
+
continue
|
|
112
|
+
|
|
113
|
+
if (plugin := load_plugin(entry_point)) is None:
|
|
114
|
+
status_str = "[red]load failed[/red]"
|
|
115
|
+
table.add_row(plugin_str, status_str, "!", "!", "!")
|
|
116
|
+
continue
|
|
117
|
+
status = (
|
|
118
|
+
"enabled"
|
|
119
|
+
if enabled_plugins and plugin_name in enabled_plugins
|
|
120
|
+
else "loaded"
|
|
121
|
+
)
|
|
122
|
+
if plugin.frontend_path is None:
|
|
123
|
+
frontend_str = ""
|
|
124
|
+
elif resolved_path := resolve_frontend(plugin):
|
|
125
|
+
size_kb = get_path_size(resolved_path) / 1024
|
|
126
|
+
frontend_str = f"{size_kb:.1f} B"
|
|
127
|
+
else:
|
|
128
|
+
frontend_str = f"[yellow]({plugin.frontend_path} not found)[/yellow]"
|
|
129
|
+
|
|
130
|
+
status_str = f"[green]{status}[/green]"
|
|
131
|
+
router_prefixes = [func.prefix for func in plugin.routers]
|
|
132
|
+
service_names = [func.__name__ for _, func in plugin.services]
|
|
133
|
+
|
|
134
|
+
table.add_row(
|
|
135
|
+
plugin_str,
|
|
136
|
+
status_str,
|
|
137
|
+
frontend_str,
|
|
138
|
+
"\n".join(router_prefixes),
|
|
139
|
+
"\n".join(service_names),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
stderr.print(table)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def get_path_size(path: Path | Traversable) -> int:
|
|
146
|
+
total_size = 0
|
|
147
|
+
for item in path.iterdir():
|
|
148
|
+
if item.is_file():
|
|
149
|
+
with item.open("rb") as f:
|
|
150
|
+
total_size += len(f.read())
|
|
151
|
+
elif item.is_dir():
|
|
152
|
+
total_size += get_path_size(item)
|
|
153
|
+
return total_size
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@app.command()
|
|
157
|
+
def init() -> None:
|
|
158
|
+
raise NotImplementedError
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
#
|
|
162
|
+
# developer utilities
|
|
163
|
+
#
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def get_package_paths(
|
|
167
|
+
extra_paths: list[Path] | None, all_plugins: bool
|
|
168
|
+
) -> Generator[tuple[str, Path], None, None]:
|
|
169
|
+
if all_plugins:
|
|
170
|
+
from .backend import get_distribution_path
|
|
171
|
+
from .plugin import scan_plugins
|
|
172
|
+
|
|
173
|
+
yield "core", get_distribution_path("tangram_core")
|
|
174
|
+
|
|
175
|
+
for ep in scan_plugins():
|
|
176
|
+
name = ep.dist.name if ep.dist else ep.name
|
|
177
|
+
yield name, get_distribution_path(name)
|
|
178
|
+
if extra_paths:
|
|
179
|
+
for path in extra_paths:
|
|
180
|
+
yield "(unknown name)", path
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@app.command()
|
|
184
|
+
def check_plugin(
|
|
185
|
+
extra_paths: Annotated[
|
|
186
|
+
list[Path] | None,
|
|
187
|
+
typer.Argument(help="Extra paths to plugin directories.", exists=True),
|
|
188
|
+
] = None,
|
|
189
|
+
all: bool = False,
|
|
190
|
+
) -> None:
|
|
191
|
+
"""Verifies that plugin `devDependencies` match those defined in the core."""
|
|
192
|
+
import json
|
|
193
|
+
|
|
194
|
+
from .backend import get_distribution_path
|
|
195
|
+
|
|
196
|
+
if extra_paths is None and not all:
|
|
197
|
+
all = True
|
|
198
|
+
|
|
199
|
+
core_path = get_distribution_path("tangram_core")
|
|
200
|
+
|
|
201
|
+
core_pkg_json = core_path / "package.json"
|
|
202
|
+
core_package_json = json.loads(core_pkg_json.read_text())
|
|
203
|
+
core_deps = core_package_json.get("dependencies", {}) | core_package_json.get(
|
|
204
|
+
"devDependencies", {}
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
has_error = False
|
|
208
|
+
for dist_name, path in get_package_paths(extra_paths, all):
|
|
209
|
+
pkg_json = path / "package.json"
|
|
210
|
+
if not pkg_json.exists():
|
|
211
|
+
continue # not all plugins have frontends, so we just skip silently
|
|
212
|
+
stderr.print(f"checking '{dist_name}' at {path}")
|
|
213
|
+
plugin_deps = json.loads(pkg_json.read_text()).get("devDependencies", {})
|
|
214
|
+
for dep, version in plugin_deps.items():
|
|
215
|
+
if dep in core_deps and core_deps[dep] != version:
|
|
216
|
+
print_error(
|
|
217
|
+
f"expected '{dep}@{core_deps[dep]}' but found '{dep}@{version}' "
|
|
218
|
+
f"in {path.name}"
|
|
219
|
+
)
|
|
220
|
+
has_error = True
|
|
221
|
+
|
|
222
|
+
if has_error:
|
|
223
|
+
raise typer.Exit(1)
|
|
224
|
+
print_success("all plugin dependencies verified")
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
RE_TOML = r'(^version\s*=\s*)"[^"]+"'
|
|
228
|
+
RE_JSON = r'("version"\s*:\s*)"[^"]+"'
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def set_version_in_file(path: Path, regex: str, version: str) -> bool:
|
|
232
|
+
if not path.exists():
|
|
233
|
+
return False
|
|
234
|
+
content = path.read_text()
|
|
235
|
+
new_content = re.sub(
|
|
236
|
+
regex,
|
|
237
|
+
f'\\1"{version}"',
|
|
238
|
+
content,
|
|
239
|
+
flags=re.MULTILINE,
|
|
240
|
+
)
|
|
241
|
+
if content != new_content:
|
|
242
|
+
path.write_text(new_content)
|
|
243
|
+
return True
|
|
244
|
+
return False
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def run_command(cmd: list[str], cwd: Path) -> None:
|
|
248
|
+
try:
|
|
249
|
+
stderr.print(f"running '{' '.join(cmd)}' in {cwd}")
|
|
250
|
+
subprocess.run(
|
|
251
|
+
cmd, cwd=cwd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE
|
|
252
|
+
)
|
|
253
|
+
except subprocess.CalledProcessError as e:
|
|
254
|
+
print_error(f"command failed in {cwd}: {' '.join(cmd)}\n{e.stderr.decode()}")
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@app.command()
|
|
258
|
+
def set_plugin_version(
|
|
259
|
+
version: str,
|
|
260
|
+
extra_path: Annotated[
|
|
261
|
+
list[Path] | None,
|
|
262
|
+
typer.Argument(help="Paths to plugins to update.", exists=True),
|
|
263
|
+
] = None,
|
|
264
|
+
all: bool = False,
|
|
265
|
+
update_lock: bool = True,
|
|
266
|
+
) -> None:
|
|
267
|
+
"""Updates version in pyproject.toml, package.json and Cargo.toml."""
|
|
268
|
+
# we allow bumping versions without bumping core.
|
|
269
|
+
cwd = Path.cwd()
|
|
270
|
+
is_in_workspace = (
|
|
271
|
+
cwd / "pnpm-workspace.yaml"
|
|
272
|
+
).exists() or "[workspace.package]" in (cwd / "Cargo.toml").read_text()
|
|
273
|
+
|
|
274
|
+
if not extra_path and not all:
|
|
275
|
+
print_error(
|
|
276
|
+
"either --all must be specified or at least one extra path must be given"
|
|
277
|
+
)
|
|
278
|
+
raise typer.Exit(1)
|
|
279
|
+
|
|
280
|
+
update_root_uv_lock = False
|
|
281
|
+
for dist_name, path in get_package_paths(extra_path, all):
|
|
282
|
+
updated_py = False
|
|
283
|
+
updated_rs = False
|
|
284
|
+
|
|
285
|
+
pyproject_toml = path / "pyproject.toml"
|
|
286
|
+
if set_version_in_file(pyproject_toml, RE_TOML, version):
|
|
287
|
+
print_success(f"updated '{dist_name}' at {pyproject_toml}")
|
|
288
|
+
updated_py = True
|
|
289
|
+
|
|
290
|
+
pkg_json = path / "package.json"
|
|
291
|
+
if set_version_in_file(pkg_json, RE_JSON, version):
|
|
292
|
+
print_success(f"updated '{dist_name}' at {pkg_json}")
|
|
293
|
+
|
|
294
|
+
cargo_toml = path / "Cargo.toml"
|
|
295
|
+
if not cargo_toml.exists() and (path / "rust" / "Cargo.toml").exists():
|
|
296
|
+
cargo_toml = path / "rust" / "Cargo.toml"
|
|
297
|
+
if cargo_toml.exists():
|
|
298
|
+
content = cargo_toml.read_text()
|
|
299
|
+
if "version.workspace = true" not in content:
|
|
300
|
+
if set_version_in_file(cargo_toml, RE_TOML, version):
|
|
301
|
+
print_success(f"updated '{dist_name}' at {cargo_toml}")
|
|
302
|
+
updated_rs = True
|
|
303
|
+
|
|
304
|
+
if update_lock:
|
|
305
|
+
if is_in_workspace: # defer
|
|
306
|
+
update_root_uv_lock = True
|
|
307
|
+
continue
|
|
308
|
+
if updated_py:
|
|
309
|
+
run_command(["uv", "lock"], path)
|
|
310
|
+
if updated_rs:
|
|
311
|
+
run_command(["cargo", "check"], cargo_toml.parent)
|
|
312
|
+
|
|
313
|
+
if is_in_workspace:
|
|
314
|
+
if update_root_uv_lock:
|
|
315
|
+
run_command(["uv", "lock"], cwd)
|
|
316
|
+
|
|
317
|
+
if not all:
|
|
318
|
+
return
|
|
319
|
+
updated_rs_ws = False
|
|
320
|
+
root_cargo = cwd / "Cargo.toml"
|
|
321
|
+
if root_cargo.exists():
|
|
322
|
+
if set_version_in_file(root_cargo, RE_TOML, version):
|
|
323
|
+
print_success(f"updated workspace '{root_cargo}'")
|
|
324
|
+
updated_rs_ws = True
|
|
325
|
+
if update_lock:
|
|
326
|
+
if updated_rs_ws:
|
|
327
|
+
run_command(["cargo", "check", "--workspace"], cwd)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
if __name__ == "__main__":
|
|
331
|
+
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
|
+
|