trmnl_preview 0.5.1 → 0.5.3
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +4 -0
- data/bin/trmnlp +4 -1
- data/lib/markup/custom_liquid_filters.rb +36 -0
- data/lib/markup/inline_templates_file_system.rb +19 -0
- data/lib/markup/template.rb +17 -0
- data/lib/markup/template_tag.rb +31 -0
- data/lib/trmnlp/commands/login.rb +1 -0
- data/lib/trmnlp/config/project.rb +2 -0
- data/lib/trmnlp/context.rb +20 -6
- data/lib/trmnlp/paths.rb +2 -0
- data/lib/trmnlp/version.rb +1 -1
- data/trmnl_preview.gemspec +2 -0
- data/web/public/index.css +0 -45
- data/web/public/index.js +16 -11
- data/web/public/trmnl-component.js +782 -0
- data/web/views/index.erb +7 -8
- data/web/views/render_html.erb +1 -1
- metadata +35 -3
- data/lib/trmnlp/custom_filters.rb +0 -14
@@ -0,0 +1,782 @@
|
|
1
|
+
(function () {
|
2
|
+
// Define the HTML template as a string.
|
3
|
+
// We wrap the SVG in a container <div id="container"> to allow forwarding
|
4
|
+
// of the host element's "class" and "style" attributes.
|
5
|
+
|
6
|
+
let mainContentId = `main-content-${Math.random().toString(36).substring(2)}`;
|
7
|
+
let containerId = `container-${Math.random().toString(36).substring(2)}`;
|
8
|
+
let contentWrapperId = `container-wrapper-${Math.random()
|
9
|
+
.toString(36)
|
10
|
+
.substring(2)}`;
|
11
|
+
let trmnlComponentIframeId = `trmnl-component-iframe-${Math.random()
|
12
|
+
.toString(36)
|
13
|
+
.substring(2)}`;
|
14
|
+
|
15
|
+
const colors = {
|
16
|
+
white: {
|
17
|
+
start: "#F7F6F6",
|
18
|
+
end: "#E9E9E9",
|
19
|
+
logo: "#E9E9E9",
|
20
|
+
},
|
21
|
+
black: {
|
22
|
+
start: "#414141",
|
23
|
+
end: "#313131",
|
24
|
+
logo: "#595959",
|
25
|
+
},
|
26
|
+
mint: {
|
27
|
+
start: "#D9DED0",
|
28
|
+
end: "#CFD3C8",
|
29
|
+
logo: "#D8DCD1",
|
30
|
+
},
|
31
|
+
gray: {
|
32
|
+
start: "#696e74",
|
33
|
+
end: "#696e74",
|
34
|
+
logo: "#595959",
|
35
|
+
},
|
36
|
+
wood: {
|
37
|
+
start: "#f9e3cb",
|
38
|
+
end: "#ecd4bc",
|
39
|
+
logo: "#fdefd5",
|
40
|
+
},
|
41
|
+
};
|
42
|
+
|
43
|
+
const contentWrapperTemplate = `<!doctype html>
|
44
|
+
<html lang="en">
|
45
|
+
<head>
|
46
|
+
<link rel="stylesheet" href="https://usetrmnl.com/css/latest/plugins.css"/>
|
47
|
+
<script type="text/javascript" src="https://usetrmnl.com/js/latest/plugins.js"></script>
|
48
|
+
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
49
|
+
<meta charset="utf-8" />
|
50
|
+
<title>TRMNL</title>
|
51
|
+
</head>
|
52
|
+
<body class="trmnl" style="background-color: white !important;">
|
53
|
+
<div id="${mainContentId}">CONTENT_PLACEHOLDER</div>
|
54
|
+
</body>
|
55
|
+
</html>`;
|
56
|
+
|
57
|
+
const templateHTML = `
|
58
|
+
<style>
|
59
|
+
:host {
|
60
|
+
display: inline-block;
|
61
|
+
width: auto;
|
62
|
+
}
|
63
|
+
#${containerId} {
|
64
|
+
width: 100%;
|
65
|
+
height: auto;
|
66
|
+
}
|
67
|
+
svg {
|
68
|
+
width: 100%;
|
69
|
+
height: auto;
|
70
|
+
display: block;
|
71
|
+
}
|
72
|
+
#${mainContentId} {
|
73
|
+
width: 100%;
|
74
|
+
height: auto;
|
75
|
+
display: block;
|
76
|
+
background-opacity: 0;
|
77
|
+
}
|
78
|
+
#${trmnlComponentIframeId} {
|
79
|
+
border: none !important;
|
80
|
+
width: 800px;
|
81
|
+
height: 480px;
|
82
|
+
}
|
83
|
+
#${trmnlComponentIframeId}.dark-mode {
|
84
|
+
filter: invert(1) brightness(0.9) sepia(25%) contrast(0.75);
|
85
|
+
opacity:90%;
|
86
|
+
}
|
87
|
+
</style>
|
88
|
+
<div id="${containerId}">
|
89
|
+
<svg width="950px" height="639px" viewBox="0 0 950 639" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
90
|
+
<title>TRMNL</title>
|
91
|
+
<defs>
|
92
|
+
<linearGradient x1="50%" y1="0.295017483%" x2="50%" y2="98.7079327%" id="linearGradient-1">
|
93
|
+
<stop id="start" stop-color="#F7F6F6" offset="0%"></stop>
|
94
|
+
<stop id="end" stop-color="#E9E9E9" offset="100%"></stop>
|
95
|
+
</linearGradient>
|
96
|
+
<path d="M28.2027372,0 L826.111263,1.81366828e-15 C835.917969,-1.0537935e-15 839.474118,1.02108172 843.059303,2.93845974 C846.644488,4.85583776 849.458162,7.66951208 851.37554,11.2546973 C853.292918,14.8398824 854.314,18.3960311 854.314,28.2027372 L854.314,546.429263 C854.314,556.235969 853.292918,559.792118 851.37554,563.377303 C849.458162,566.962488 846.644488,569.776162 843.059303,571.69354 C839.474118,573.610918 835.917969,574.632 826.111263,574.632 L28.2027372,574.632 C18.3960311,574.632 14.8398824,573.610918 11.2546973,571.69354 C7.66951208,569.776162 4.85583776,566.962488 2.93845974,563.377303 C1.02108172,559.792118 1.79144895e-14,556.235969 -3.08323607e-14,546.429263 L0,28.2027372 C0,18.3960311 1.02108172,14.8398824 2.93845974,11.2546973 C4.85583776,7.66951208 7.66951208,4.85583776 11.2546973,2.93845974 C14.8398824,1.02108172 18.3960311,0 28.2027372,0 Z" id="path-2"></path>
|
97
|
+
<filter x="-1.3%" y="-1.7%" width="102.7%" height="104.0%" filterUnits="objectBoundingBox" id="filter-3">
|
98
|
+
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
99
|
+
<feGaussianBlur stdDeviation="3.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
100
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.342083698 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
|
101
|
+
</filter>
|
102
|
+
<path d="M38.5374834,27 L815.462517,27 C819.474351,27 820.929139,27.4177153 822.395806,28.2020972 C823.862472,28.9864791 825.013521,30.1375277 825.797903,31.6041943 C826.582285,33.070861 827,34.5256491 827,38.5374834 L827,495.462517 C827,499.474351 826.582285,500.929139 825.797903,502.395806 C825.013521,503.862472 823.862472,505.013521 822.395806,505.797903 C820.929139,506.582285 819.474351,507 815.462517,507 L38.5374834,507 C34.5256491,507 33.070861,506.582285 31.6041943,505.797903 C30.1375277,505.013521 28.9864791,503.862472 28.2020972,502.395806 C27.4177153,500.929139 27,499.474351 27,495.462517 L27,38.5374834 C27,34.5256491 27.4177153,33.070861 28.2020972,31.6041943 C28.9864791,30.1375277 30.1375277,28.9864791 31.6041943,28.2020972 C33.070861,27.4177153 34.5256491,27 38.5374834,27 Z" id="path-4"></path>
|
103
|
+
<filter x="-0.5%" y="-0.8%" width="101.0%" height="101.7%" filterUnits="objectBoundingBox" id="filter-5">
|
104
|
+
<feGaussianBlur stdDeviation="3" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
105
|
+
<feOffset dx="0" dy="2" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
106
|
+
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
107
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
108
|
+
</filter>
|
109
|
+
<polygon id="path-6" points="30.4432432 6.94287907 30.4432432 4.15135135 46.3567568 4.15135135 46.3567568 6.94287907 39.9198299 6.94287907 39.9198299 18.6810811 36.8990438 18.6810811 36.8990438 6.94287907"></polygon>
|
110
|
+
<filter x="-9.4%" y="-10.3%" width="118.9%" height="120.6%" filterUnits="objectBoundingBox" id="filter-7">
|
111
|
+
<feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
112
|
+
<feOffset dx="0" dy="1" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
113
|
+
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
114
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
115
|
+
</filter>
|
116
|
+
<path d="M49.8162162,4.15135135 L49.8162162,18.6810811 L52.8523226,18.6810811 L52.8523226,13.5912453 L61.0721342,13.5912453 C61.6443807,13.5912453 62.0184369,13.7189233 62.2506098,13.9435637 C62.4783178,14.1639612 62.6184648,14.5256765 62.6184648,15.1152338 L62.6184648,18.6810811 L65.6357196,18.6810811 L65.6357196,14.7403994 C65.6357196,13.8468096 65.3447594,13.1148672 64.814929,12.600511 C64.5115665,12.3061097 64.1385024,12.0909964 63.7133483,11.9555874 C64.2208543,11.696457 64.6412955,11.3548891 64.9652461,10.932914 C65.4762248,10.2674527 65.7297297,9.42496011 65.7297297,8.44713669 C65.7297297,7.06980219 65.2916771,5.97468682 64.3582728,5.23449284 C63.4382632,4.50512742 62.0749919,4.15135135 60.2825977,4.15135135 L49.8162162,4.15135135 Z M59.75624,10.8786151 L52.8523226,10.8786151 L52.8523226,6.84422472 L59.75624,6.84422472 C60.760834,6.84422472 61.4784366,6.9832779 61.9403018,7.28832549 C62.3756259,7.57590683 62.6184648,8.03546952 62.6184648,8.8022403 C62.6184648,9.5814535 62.3733935,10.0741526 61.9328604,10.3878162 C61.4714913,10.7164471 60.7551289,10.8786151 59.75624,10.8786151 Z" id="path-8"></path>
|
117
|
+
<filter x="-9.4%" y="-10.3%" width="118.9%" height="120.6%" filterUnits="objectBoundingBox" id="filter-9">
|
118
|
+
<feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
119
|
+
<feOffset dx="0" dy="1" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
120
|
+
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
121
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
122
|
+
</filter>
|
123
|
+
<polygon id="path-10" points="68.4972973 18.6810811 68.4972973 4.15135135 72.9082435 4.15135135 79.241009 15.0102802 85.554257 4.15135135 89.9459459 4.15135135 89.9459459 18.6810811 86.7802138 18.6810811 86.7802138 7.96393861 80.6210236 18.6810811 77.8417371 18.6810811 71.6825469 7.96393861 71.6825469 18.6810811"></polygon>
|
124
|
+
<filter x="-7.0%" y="-10.3%" width="114.0%" height="120.6%" filterUnits="objectBoundingBox" id="filter-11">
|
125
|
+
<feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
126
|
+
<feOffset dx="0" dy="1" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
127
|
+
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
128
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
129
|
+
</filter>
|
130
|
+
<polygon id="path-12" points="97.435008 4.15135135 93.4054054 4.15135135 93.4054054 18.6810811 96.4068254 18.6810811 96.4068254 7.50994637 105.999845 18.6810811 110.010811 18.6810811 110.010811 4.15135135 107.028027 4.15135135 107.028027 15.3224861"></polygon>
|
131
|
+
<filter x="-9.0%" y="-10.3%" width="118.1%" height="120.6%" filterUnits="objectBoundingBox" id="filter-13">
|
132
|
+
<feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
133
|
+
<feOffset dx="0" dy="1" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
134
|
+
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
135
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
136
|
+
</filter>
|
137
|
+
<polygon id="path-14" points="117.380019 4.15135135 114.162162 4.15135135 114.162162 18.6810811 128 18.6810811 128 15.8895534 117.380019 15.8895534"></polygon>
|
138
|
+
<filter x="-10.8%" y="-10.3%" width="121.7%" height="120.6%" filterUnits="objectBoundingBox" id="filter-15">
|
139
|
+
<feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
140
|
+
<feOffset dx="0" dy="1" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
141
|
+
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
142
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
143
|
+
</filter>
|
144
|
+
<polygon id="path-16" points="6.1435832 0.691891892 13.1459459 3.37533695 11.845606 6.91891892 4.84324324 4.2354993"></polygon>
|
145
|
+
<filter x="-18.1%" y="-24.1%" width="136.1%" height="148.2%" filterUnits="objectBoundingBox" id="filter-17">
|
146
|
+
<feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
147
|
+
<feOffset dx="0" dy="1" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
148
|
+
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
149
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
150
|
+
</filter>
|
151
|
+
<polygon id="path-18" points="17.5964512 0 20.0648649 7.15103824 16.3062515 8.3027027 13.8378378 1.15166697"></polygon>
|
152
|
+
<filter x="-24.1%" y="-18.1%" width="148.2%" height="136.1%" filterUnits="objectBoundingBox" id="filter-19">
|
153
|
+
<feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
154
|
+
<feOffset dx="0" dy="1" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
155
|
+
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
156
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
157
|
+
</filter>
|
158
|
+
<polygon id="path-20" points="25.6 8.27152584 21.2525567 14.5297297 17.9891892 12.4852309 22.3366325 6.22702703"></polygon>
|
159
|
+
<filter x="-19.7%" y="-18.1%" width="139.4%" height="136.1%" filterUnits="objectBoundingBox" id="filter-21">
|
160
|
+
<feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
161
|
+
<feOffset dx="0" dy="1" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
162
|
+
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
163
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
164
|
+
</filter>
|
165
|
+
<polygon id="path-22" points="22.8324324 20.0099988 15.5472692 20.7567568 15.2216216 16.6602715 22.5067605 15.9135135"></polygon>
|
166
|
+
<filter x="-19.7%" y="-31.0%" width="139.4%" height="161.9%" filterUnits="objectBoundingBox" id="filter-23">
|
167
|
+
<feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
168
|
+
<feOffset dx="0" dy="1" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
169
|
+
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
170
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
171
|
+
</filter>
|
172
|
+
<polygon id="path-24" points="14.009405 24.9081081 8.99459459 19.2742777 11.590595 16.6054054 16.6054054 22.2392877"></polygon>
|
173
|
+
<filter x="-19.7%" y="-18.1%" width="139.4%" height="136.1%" filterUnits="objectBoundingBox" id="filter-25">
|
174
|
+
<feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
175
|
+
<feOffset dx="0" dy="1" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
176
|
+
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
177
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
178
|
+
</filter>
|
179
|
+
<polygon id="path-26" points="2.76756757 20.9711149 3.9545589 13.8378378 8.3027027 14.3153716 7.11571137 21.4486486"></polygon>
|
180
|
+
<filter x="-27.1%" y="-19.7%" width="154.2%" height="139.4%" filterUnits="objectBoundingBox" id="filter-27">
|
181
|
+
<feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
182
|
+
<feOffset dx="0" dy="1" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
183
|
+
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
184
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
185
|
+
</filter>
|
186
|
+
<polygon id="path-28" points="0 10.0113136 6.40614303 6.22702703 8.3027027 9.36165939 1.89655719 13.1459459"></polygon>
|
187
|
+
<filter x="-18.1%" y="-21.7%" width="136.1%" height="143.4%" filterUnits="objectBoundingBox" id="filter-29">
|
188
|
+
<feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
|
189
|
+
<feOffset dx="0" dy="1" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
|
190
|
+
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
|
191
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
|
192
|
+
</filter>
|
193
|
+
</defs>
|
194
|
+
<g id="Artboard" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" >
|
195
|
+
<g id="Group" transform="translate(48, 32)">
|
196
|
+
<g id="Rectangle">
|
197
|
+
<use fill="black" fill-opacity="1" filter="url(#filter-3)" xlink:href="#path-2"></use>
|
198
|
+
<use fill="url(#linearGradient-1)" fill-rule="evenodd" xlink:href="#path-2"></use>
|
199
|
+
</g>
|
200
|
+
<g id="Rectangle">
|
201
|
+
<use fill="#E4E2E1" fill-rule="evenodd" xlink:href="#path-4"></use>
|
202
|
+
<use fill="black" fill-opacity="1" filter="url(#filter-5)" xlink:href="#path-4"></use>
|
203
|
+
</g>
|
204
|
+
|
205
|
+
|
206
|
+
|
207
|
+
<foreignobject class="node" x="36" y="34" width="840" height="520"
|
208
|
+
style="transform:scale(0.98); position: relative; border-radius: 12px; opacity: 0.9; mix-blend-mode: darken;">
|
209
|
+
<div id="${contentWrapperId}"
|
210
|
+
style="position: static; width: 100%; height: 100%; max-width: 100%; max-height: 100%;">
|
211
|
+
<iframe id="${trmnlComponentIframeId}" src="about:blank"
|
212
|
+
style="display:block; position: static; top: 0; left: 0; width: 100%; height: 100%; border: none;"></iframe>
|
213
|
+
</div>
|
214
|
+
</foreignobject>
|
215
|
+
|
216
|
+
<g id="logo--brand@vector" transform="translate(363, 529)">
|
217
|
+
<g id="Path">
|
218
|
+
<use
|
219
|
+
class="logo__path"
|
220
|
+
fill="#E9E9E9"
|
221
|
+
fill-rule="evenodd"
|
222
|
+
xlink:href="#path-6"
|
223
|
+
></use>
|
224
|
+
<use
|
225
|
+
fill="black"
|
226
|
+
fill-opacity="1"
|
227
|
+
filter="url(#filter-7)"
|
228
|
+
xlink:href="#path-6"
|
229
|
+
></use>
|
230
|
+
</g>
|
231
|
+
<g id="Shape">
|
232
|
+
<use
|
233
|
+
class="logo__path"
|
234
|
+
fill="#E9E9E9"
|
235
|
+
fill-rule="evenodd"
|
236
|
+
xlink:href="#path-8"
|
237
|
+
></use>
|
238
|
+
<use
|
239
|
+
fill="black"
|
240
|
+
fill-opacity="1"
|
241
|
+
filter="url(#filter-9)"
|
242
|
+
xlink:href="#path-8"
|
243
|
+
></use>
|
244
|
+
</g>
|
245
|
+
<g id="Path">
|
246
|
+
<use
|
247
|
+
class="logo__path"
|
248
|
+
fill="#E9E9E9"
|
249
|
+
fill-rule="evenodd"
|
250
|
+
xlink:href="#path-10"
|
251
|
+
></use>
|
252
|
+
<use
|
253
|
+
fill="black"
|
254
|
+
fill-opacity="1"
|
255
|
+
filter="url(#filter-11)"
|
256
|
+
xlink:href="#path-10"
|
257
|
+
></use>
|
258
|
+
</g>
|
259
|
+
<g id="Path">
|
260
|
+
<use
|
261
|
+
class="logo__path"
|
262
|
+
fill="#E9E9E9"
|
263
|
+
fill-rule="evenodd"
|
264
|
+
xlink:href="#path-12"
|
265
|
+
></use>
|
266
|
+
<use
|
267
|
+
fill="black"
|
268
|
+
fill-opacity="1"
|
269
|
+
filter="url(#filter-13)"
|
270
|
+
xlink:href="#path-12"
|
271
|
+
></use>
|
272
|
+
</g>
|
273
|
+
<g id="Path">
|
274
|
+
<use
|
275
|
+
class="logo__path"
|
276
|
+
fill="#E9E9E9"
|
277
|
+
fill-rule="evenodd"
|
278
|
+
xlink:href="#path-14"
|
279
|
+
></use>
|
280
|
+
<use
|
281
|
+
fill="black"
|
282
|
+
fill-opacity="1"
|
283
|
+
filter="url(#filter-15)"
|
284
|
+
xlink:href="#path-14"
|
285
|
+
></use>
|
286
|
+
</g>
|
287
|
+
<g id="Path">
|
288
|
+
<use
|
289
|
+
class="logo__path"
|
290
|
+
fill="#E9E9E9"
|
291
|
+
fill-rule="evenodd"
|
292
|
+
xlink:href="#path-16"
|
293
|
+
></use>
|
294
|
+
<use
|
295
|
+
fill="black"
|
296
|
+
fill-opacity="1"
|
297
|
+
filter="url(#filter-17)"
|
298
|
+
xlink:href="#path-16"
|
299
|
+
></use>
|
300
|
+
</g>
|
301
|
+
<g id="Path">
|
302
|
+
<use
|
303
|
+
class="logo__path"
|
304
|
+
fill="#E9E9E9"
|
305
|
+
fill-rule="evenodd"
|
306
|
+
xlink:href="#path-18"
|
307
|
+
></use>
|
308
|
+
<use
|
309
|
+
fill="black"
|
310
|
+
fill-opacity="1"
|
311
|
+
filter="url(#filter-19)"
|
312
|
+
xlink:href="#path-18"
|
313
|
+
></use>
|
314
|
+
</g>
|
315
|
+
<g id="Path">
|
316
|
+
<use
|
317
|
+
class="logo__path"
|
318
|
+
fill="#E9E9E9"
|
319
|
+
fill-rule="evenodd"
|
320
|
+
xlink:href="#path-20"
|
321
|
+
></use>
|
322
|
+
<use
|
323
|
+
fill="black"
|
324
|
+
fill-opacity="1"
|
325
|
+
filter="url(#filter-21)"
|
326
|
+
xlink:href="#path-20"
|
327
|
+
></use>
|
328
|
+
</g>
|
329
|
+
<g id="Path">
|
330
|
+
<use
|
331
|
+
class="logo__path"
|
332
|
+
fill="#E9E9E9"
|
333
|
+
fill-rule="evenodd"
|
334
|
+
xlink:href="#path-22"
|
335
|
+
></use>
|
336
|
+
<use
|
337
|
+
fill="black"
|
338
|
+
fill-opacity="1"
|
339
|
+
filter="url(#filter-23)"
|
340
|
+
xlink:href="#path-22"
|
341
|
+
></use>
|
342
|
+
</g>
|
343
|
+
<g id="Path">
|
344
|
+
<use
|
345
|
+
class="logo__path"
|
346
|
+
fill="#E9E9E9"
|
347
|
+
fill-rule="evenodd"
|
348
|
+
xlink:href="#path-24"
|
349
|
+
></use>
|
350
|
+
<use
|
351
|
+
fill="black"
|
352
|
+
fill-opacity="1"
|
353
|
+
filter="url(#filter-25)"
|
354
|
+
xlink:href="#path-24"
|
355
|
+
></use>
|
356
|
+
</g>
|
357
|
+
<g id="Path">
|
358
|
+
<use
|
359
|
+
class="logo__path"
|
360
|
+
fill="#E9E9E9"
|
361
|
+
fill-rule="evenodd"
|
362
|
+
xlink:href="#path-26"
|
363
|
+
></use>
|
364
|
+
<use
|
365
|
+
fill="black"
|
366
|
+
fill-opacity="1"
|
367
|
+
filter="url(#filter-27)"
|
368
|
+
xlink:href="#path-26"
|
369
|
+
></use>
|
370
|
+
</g>
|
371
|
+
<g id="Path">
|
372
|
+
<use
|
373
|
+
class="logo__path"
|
374
|
+
fill="#E9E9E9"
|
375
|
+
fill-rule="evenodd"
|
376
|
+
xlink:href="#path-28"
|
377
|
+
></use>
|
378
|
+
<use
|
379
|
+
fill="black"
|
380
|
+
fill-opacity="1"
|
381
|
+
filter="url(#filter-29)"
|
382
|
+
xlink:href="#path-28"
|
383
|
+
></use>
|
384
|
+
</g>
|
385
|
+
</g>
|
386
|
+
</g>
|
387
|
+
</g>
|
388
|
+
</svg>
|
389
|
+
</div>
|
390
|
+
`;
|
391
|
+
|
392
|
+
class TRMNL extends HTMLElement {
|
393
|
+
constructor() {
|
394
|
+
super();
|
395
|
+
console.log("TRMNL element constructed.");
|
396
|
+
this._src = null; // Store the src attribute value
|
397
|
+
this._color = "white"; // Default color
|
398
|
+
this._contentMode = "external"; // Default to external src mode
|
399
|
+
this._htmlContent = ""; // Store HTML content
|
400
|
+
this._darkMode = null; // Store dark mode setting
|
401
|
+
|
402
|
+
// Attach a shadow root and append the template content
|
403
|
+
const shadow = this.attachShadow({ mode: "open" });
|
404
|
+
const template = document.createElement("template");
|
405
|
+
template.innerHTML = templateHTML;
|
406
|
+
shadow.appendChild(template.content.cloneNode(true));
|
407
|
+
|
408
|
+
// Get reference to the iframe for later use
|
409
|
+
this._iframe = this.shadowRoot.querySelector(
|
410
|
+
`#${trmnlComponentIframeId}`
|
411
|
+
);
|
412
|
+
|
413
|
+
// Setup iframe load handler once
|
414
|
+
if (this._iframe) {
|
415
|
+
this._iframe.onload = () => {
|
416
|
+
this._handleIframeLoaded();
|
417
|
+
};
|
418
|
+
}
|
419
|
+
|
420
|
+
// Process any slotted content
|
421
|
+
this._processSlottedContent();
|
422
|
+
|
423
|
+
// Initialize dark mode media query listener if needed
|
424
|
+
this._darkModeMediaQuery = window.matchMedia(
|
425
|
+
"(prefers-color-scheme: dark)"
|
426
|
+
);
|
427
|
+
this._darkModeMediaQuery.addEventListener("change", () => {
|
428
|
+
this._updateDarkMode();
|
429
|
+
});
|
430
|
+
}
|
431
|
+
|
432
|
+
_handleIframeLoaded() {
|
433
|
+
if (this._contentMode === "internal" && this._htmlContent) {
|
434
|
+
try {
|
435
|
+
const currentSrc = this._iframe.getAttribute("src");
|
436
|
+
if (currentSrc === "about:blank") {
|
437
|
+
const iframeDoc = this._iframe.contentWindow.document;
|
438
|
+
|
439
|
+
// Step 1: Write the base shell with placeholder container
|
440
|
+
iframeDoc.open();
|
441
|
+
iframeDoc.write(
|
442
|
+
contentWrapperTemplate.replace(
|
443
|
+
`<div id="${mainContentId}">CONTENT_PLACEHOLDER</div>`,
|
444
|
+
`<div id="${mainContentId}"></div>`
|
445
|
+
)
|
446
|
+
);
|
447
|
+
iframeDoc.close();
|
448
|
+
|
449
|
+
// Step 2: Wait for the contentWrapperTemplate to parse & render
|
450
|
+
this._iframe.contentWindow.addEventListener(
|
451
|
+
"DOMContentLoaded",
|
452
|
+
() => {
|
453
|
+
const container = iframeDoc.getElementById(mainContentId);
|
454
|
+
if (!container) {
|
455
|
+
console.warn(
|
456
|
+
`${mainContentId} container not found inside iframe`
|
457
|
+
);
|
458
|
+
return;
|
459
|
+
}
|
460
|
+
|
461
|
+
// Step 3: Apply the transition & inject content
|
462
|
+
// Check if not Safari and if startViewTransition is available
|
463
|
+
const isSafari = /^((?!chrome|android).)*safari/i.test(
|
464
|
+
navigator.userAgent
|
465
|
+
);
|
466
|
+
if (
|
467
|
+
!isSafari &&
|
468
|
+
typeof iframeDoc.startViewTransition === "function"
|
469
|
+
) {
|
470
|
+
iframeDoc.startViewTransition(() => {
|
471
|
+
container.innerHTML = this._htmlContent;
|
472
|
+
});
|
473
|
+
} else {
|
474
|
+
container.innerHTML = this._htmlContent;
|
475
|
+
}
|
476
|
+
|
477
|
+
console.log(
|
478
|
+
"Dynamic content injected" +
|
479
|
+
(!isSafari ? " with View Transition" : "")
|
480
|
+
);
|
481
|
+
},
|
482
|
+
{ once: true }
|
483
|
+
);
|
484
|
+
|
485
|
+
const isSafari = /^((?!chrome|android).)*safari/i.test(
|
486
|
+
navigator.userAgent
|
487
|
+
);
|
488
|
+
if (isSafari) {
|
489
|
+
console.log("Safari browser detected");
|
490
|
+
}
|
491
|
+
// Safari needs additional help to render content correctly
|
492
|
+
if (isSafari) {
|
493
|
+
const container = iframeDoc.getElementById(mainContentId);
|
494
|
+
|
495
|
+
setTimeout(() => {
|
496
|
+
if (container && this._htmlContent) {
|
497
|
+
container.innerHTML = this._htmlContent;
|
498
|
+
console.log(
|
499
|
+
"Applied additional Safari-specific content update"
|
500
|
+
);
|
501
|
+
}
|
502
|
+
}, 100);
|
503
|
+
}
|
504
|
+
}
|
505
|
+
} catch (e) {
|
506
|
+
console.error("Error injecting HTML with transition into iframe:", e);
|
507
|
+
}
|
508
|
+
}
|
509
|
+
}
|
510
|
+
|
511
|
+
static get observedAttributes() {
|
512
|
+
return ["src", "class", "style", "color", "dark"];
|
513
|
+
}
|
514
|
+
|
515
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
516
|
+
if (name === "src") {
|
517
|
+
console.log(
|
518
|
+
`attributeChangedCallback: 'src' changed from ${oldValue} to ${newValue}`
|
519
|
+
);
|
520
|
+
this._src = newValue;
|
521
|
+
this._contentMode = "external";
|
522
|
+
this.updateIframe();
|
523
|
+
} else if (name === "class" || name === "style") {
|
524
|
+
this.updateContainerAttribute(name, newValue);
|
525
|
+
} else if (name === "color") {
|
526
|
+
this._color = newValue || "white"; // Default to white if not specified
|
527
|
+
this.updateColors();
|
528
|
+
} else if (name === "dark") {
|
529
|
+
this._darkMode = newValue;
|
530
|
+
this._updateDarkMode();
|
531
|
+
}
|
532
|
+
}
|
533
|
+
|
534
|
+
connectedCallback() {
|
535
|
+
console.log("TRMNL connectedCallback invoked.");
|
536
|
+
|
537
|
+
// Process any existing content from innerHTML
|
538
|
+
this._processSlottedContent();
|
539
|
+
|
540
|
+
if (this.hasAttribute("src")) {
|
541
|
+
const src = this.getAttribute("src");
|
542
|
+
console.log("connectedCallback found src attribute:", src);
|
543
|
+
this._src = src;
|
544
|
+
this._contentMode = "external";
|
545
|
+
this.updateIframe();
|
546
|
+
}
|
547
|
+
|
548
|
+
// Initialize container attributes for class and style
|
549
|
+
this.updateContainerAttribute("class", this.getAttribute("class"));
|
550
|
+
this.updateContainerAttribute("style", this.getAttribute("style"));
|
551
|
+
|
552
|
+
// Initialize color
|
553
|
+
if (this.hasAttribute("color")) {
|
554
|
+
this._color = this.getAttribute("color");
|
555
|
+
}
|
556
|
+
this.updateColors();
|
557
|
+
|
558
|
+
// Initialize dark mode
|
559
|
+
if (this.hasAttribute("dark")) {
|
560
|
+
this._darkMode = this.getAttribute("dark");
|
561
|
+
this._updateDarkMode();
|
562
|
+
}
|
563
|
+
|
564
|
+
// Set up the mutation observer to handle content changes
|
565
|
+
this._setupMutationObserver();
|
566
|
+
}
|
567
|
+
|
568
|
+
disconnectedCallback() {
|
569
|
+
// Clean up the mutation observer when element is removed
|
570
|
+
if (this._observer) {
|
571
|
+
this._observer.disconnect();
|
572
|
+
}
|
573
|
+
|
574
|
+
// Remove media query listener
|
575
|
+
if (this._darkModeMediaQuery) {
|
576
|
+
this._darkModeMediaQuery.removeEventListener("change", () => {
|
577
|
+
this._updateDarkMode();
|
578
|
+
});
|
579
|
+
}
|
580
|
+
}
|
581
|
+
|
582
|
+
_updateDarkMode() {
|
583
|
+
if (!this._iframe) return;
|
584
|
+
|
585
|
+
// Step 1: Remove the class
|
586
|
+
this._iframe.classList.remove("dark-mode");
|
587
|
+
|
588
|
+
// Step 2: Force reflow by reading offsetHeight
|
589
|
+
// This ensures the class removal is flushed
|
590
|
+
void this._iframe.offsetHeight;
|
591
|
+
|
592
|
+
// Step 3: Re-apply the class based on logic
|
593
|
+
const shouldEnableDark =
|
594
|
+
this._darkMode === "true" ||
|
595
|
+
(this._darkMode === "auto" && this._darkModeMediaQuery.matches);
|
596
|
+
|
597
|
+
if (shouldEnableDark) {
|
598
|
+
this._iframe.classList.add("dark-mode");
|
599
|
+
}
|
600
|
+
}
|
601
|
+
|
602
|
+
_setupMutationObserver() {
|
603
|
+
// Create a MutationObserver to detect content changes
|
604
|
+
this._observer = new MutationObserver((mutations) => {
|
605
|
+
mutations.forEach((mutation) => {
|
606
|
+
if (mutation.type === "childList") {
|
607
|
+
this._processSlottedContent();
|
608
|
+
}
|
609
|
+
});
|
610
|
+
});
|
611
|
+
|
612
|
+
// Observe changes to the element's child nodes
|
613
|
+
this._observer.observe(this, { childList: true });
|
614
|
+
}
|
615
|
+
|
616
|
+
_processSlottedContent() {
|
617
|
+
// Check if there's content inside the tags
|
618
|
+
if (this.innerHTML.trim()) {
|
619
|
+
// Set content mode to internal and store HTML
|
620
|
+
this._contentMode = "internal";
|
621
|
+
this._htmlContent = this.innerHTML;
|
622
|
+
this._prepareThenInjectHTML();
|
623
|
+
}
|
624
|
+
}
|
625
|
+
|
626
|
+
_prepareThenInjectHTML() {
|
627
|
+
if (!this._iframe) {
|
628
|
+
console.error("Iframe not found in shadow DOM");
|
629
|
+
return;
|
630
|
+
}
|
631
|
+
|
632
|
+
// Set iframe to about:blank to ensure same-origin
|
633
|
+
// This will trigger the onload handler which will inject the content
|
634
|
+
this._iframe.setAttribute("src", "about:blank");
|
635
|
+
}
|
636
|
+
|
637
|
+
updateIframe() {
|
638
|
+
// Delay updating the iframe until the next animation frame
|
639
|
+
requestAnimationFrame(() => {
|
640
|
+
if (this._iframe) {
|
641
|
+
if (this._contentMode === "external" && this._src) {
|
642
|
+
this._iframe.setAttribute("src", this._src);
|
643
|
+
console.log(`iframe src set to: ${this._src}`);
|
644
|
+
} else if (this._contentMode === "internal") {
|
645
|
+
// For internal content, ensure we're at about:blank
|
646
|
+
this._prepareThenInjectHTML();
|
647
|
+
}
|
648
|
+
} else {
|
649
|
+
console.error(
|
650
|
+
"iframe element not found in shadow DOM during updateIframe."
|
651
|
+
);
|
652
|
+
}
|
653
|
+
});
|
654
|
+
}
|
655
|
+
|
656
|
+
updateContainerAttribute(name, value) {
|
657
|
+
const container = this.shadowRoot.querySelector("#container");
|
658
|
+
if (container) {
|
659
|
+
if (value !== null) {
|
660
|
+
container.setAttribute(name, value);
|
661
|
+
} else {
|
662
|
+
container.removeAttribute(name);
|
663
|
+
}
|
664
|
+
}
|
665
|
+
}
|
666
|
+
|
667
|
+
updateColors() {
|
668
|
+
// Validate that the color exists in our colors object
|
669
|
+
if (!colors[this._color]) {
|
670
|
+
console.warn(`Color "${this._color}" not found, defaulting to white`);
|
671
|
+
this._color = "white";
|
672
|
+
}
|
673
|
+
|
674
|
+
const colorSet = colors[this._color];
|
675
|
+
|
676
|
+
// Update the gradient stops
|
677
|
+
const startStop = this.shadowRoot.querySelector("#start");
|
678
|
+
const endStop = this.shadowRoot.querySelector("#end");
|
679
|
+
|
680
|
+
if (startStop && endStop) {
|
681
|
+
startStop.setAttribute("stop-color", colorSet.start);
|
682
|
+
endStop.setAttribute("stop-color", colorSet.end);
|
683
|
+
console.log(`Updated gradient colors to ${this._color} theme`);
|
684
|
+
}
|
685
|
+
|
686
|
+
// Update all SVG elements in the logo that use the fill color
|
687
|
+
const logoGroup = this.shadowRoot.querySelector("#logo--brand\\@vector");
|
688
|
+
if (logoGroup) {
|
689
|
+
// Find all 'use' elements with fill="#E9E9E9" and update them
|
690
|
+
const logoElements = logoGroup.querySelectorAll(".logo__path");
|
691
|
+
logoElements.forEach((element) => {
|
692
|
+
element.setAttribute("fill", colorSet.logo);
|
693
|
+
});
|
694
|
+
}
|
695
|
+
}
|
696
|
+
|
697
|
+
// JavaScript API methods
|
698
|
+
|
699
|
+
/**
|
700
|
+
* Set the color theme: 'white', 'black', or 'mint'
|
701
|
+
* @param {string} color - The color theme to use
|
702
|
+
*/
|
703
|
+
setColor(color) {
|
704
|
+
this._color = color;
|
705
|
+
this.updateColors();
|
706
|
+
return this; // For chaining
|
707
|
+
}
|
708
|
+
|
709
|
+
/**
|
710
|
+
* Set dark mode: 'true', 'false', or 'auto'
|
711
|
+
* @param {string} mode - The dark mode setting
|
712
|
+
*/
|
713
|
+
setDark(mode) {
|
714
|
+
this._darkMode = mode;
|
715
|
+
this._updateDarkMode();
|
716
|
+
return this; // For chaining
|
717
|
+
}
|
718
|
+
|
719
|
+
/**
|
720
|
+
* Set HTML content directly, wrapping it in the template
|
721
|
+
* @param {string} html - HTML content to display
|
722
|
+
*/
|
723
|
+
setHTML(html) {
|
724
|
+
this._contentMode = "internal";
|
725
|
+
this._htmlContent = html;
|
726
|
+
this._prepareThenInjectHTML();
|
727
|
+
return this; // For chaining
|
728
|
+
}
|
729
|
+
|
730
|
+
/**
|
731
|
+
* Clear all content
|
732
|
+
*/
|
733
|
+
clearContent() {
|
734
|
+
if (this._iframe) {
|
735
|
+
this._htmlContent = "";
|
736
|
+
|
737
|
+
if (this._contentMode === "internal") {
|
738
|
+
// Reset to about:blank to clear content
|
739
|
+
this._iframe.setAttribute("src", "about:blank");
|
740
|
+
} else {
|
741
|
+
// For external mode, set src to empty
|
742
|
+
this._src = "";
|
743
|
+
this._iframe.setAttribute("src", "about:blank");
|
744
|
+
}
|
745
|
+
}
|
746
|
+
return this; // For chaining
|
747
|
+
}
|
748
|
+
|
749
|
+
/**
|
750
|
+
* Set iframe source to an external URL
|
751
|
+
* @param {string} src - URL to load in the iframe
|
752
|
+
*/
|
753
|
+
setSrc(src) {
|
754
|
+
this._contentMode = "external";
|
755
|
+
this._src = src;
|
756
|
+
this.updateIframe();
|
757
|
+
return this; // For chaining
|
758
|
+
}
|
759
|
+
|
760
|
+
/**
|
761
|
+
* Get current HTML content (for internal content mode only)
|
762
|
+
* @returns {string} The current HTML content or empty string if in external mode
|
763
|
+
*/
|
764
|
+
getHTML() {
|
765
|
+
// Simply return the stored HTML content
|
766
|
+
return this._contentMode === "internal" ? this._htmlContent : "";
|
767
|
+
}
|
768
|
+
|
769
|
+
/**
|
770
|
+
* Set dark mode: 'true', 'false', or 'auto'
|
771
|
+
* @param {string} mode - The dark mode setting
|
772
|
+
*/
|
773
|
+
setDarkMode(mode) {
|
774
|
+
this._darkMode = mode;
|
775
|
+
this._updateDarkMode();
|
776
|
+
return this; // For chaining
|
777
|
+
}
|
778
|
+
}
|
779
|
+
|
780
|
+
// Register the custom element using a hyphenated tag name
|
781
|
+
customElements.define("trmnl-frame", TRMNL);
|
782
|
+
})();
|