1 | <!DOCTYPE html>
|
2 | <html>
|
3 | <head>
|
4 | <title>React Developer Tools</title>
|
5 | <meta charset="utf8" />
|
6 | <style>
|
7 | html {
|
8 | height: 100%;
|
9 | font-family: sans-serif;
|
10 | }
|
11 | body {
|
12 | height: 100%;
|
13 | margin: 0;
|
14 | padding: 0;
|
15 | background-color: #fff;
|
16 | color: #777d88;
|
17 | }
|
18 |
|
19 | .container {
|
20 | height: 100%;
|
21 | display: flex;
|
22 | flex-direction: column;
|
23 | align-items: center;
|
24 | justify-content: center;
|
25 | overflow: auto;
|
26 | }
|
27 |
|
28 | p {
|
29 | padding: 0;
|
30 | margin: 0;
|
31 | }
|
32 |
|
33 | .input {
|
34 | display: block;
|
35 | font-weight: 100;
|
36 | padding: 0 0.25rem;
|
37 | border: 1px solid #aaa;
|
38 | background-color: #fff;
|
39 | color: #666;
|
40 | }
|
41 |
|
42 | .link {
|
43 | color: #1478fa;
|
44 | text-decoration: none;
|
45 | }
|
46 | .link:hover {
|
47 | text-decoration: underline;
|
48 | }
|
49 |
|
50 | .waiting-header {
|
51 | padding: 0.5rem;
|
52 | display: inline-block;
|
53 | position: absolute;
|
54 | right: 0.5rem;
|
55 | top: 0.5rem;
|
56 | border-radius: 0.25rem;
|
57 | background-color: rgba(0,1,2,.6);
|
58 | color: white;
|
59 | border: none;
|
60 | font-weight: 100;
|
61 | font-style: italic;
|
62 | }
|
63 |
|
64 | .boxes {
|
65 | display: flex;
|
66 | flex-direction: column;
|
67 | align-items: stretch;
|
68 | justify-content: center;
|
69 | padding: 1rem;
|
70 | }
|
71 | .box {
|
72 | text-align: center;
|
73 | border-radius: 0.5rem;
|
74 | background-color: #f7f7f7;
|
75 | border: 1px solid #eee;
|
76 | color: #777d88;
|
77 | padding: 1rem;
|
78 | margin-top: 1rem;
|
79 | }
|
80 | .box:first-of-type {
|
81 | margin-top: 0;
|
82 | }
|
83 |
|
84 | .box-header {
|
85 | text-align: center;
|
86 | color: #5f6673;
|
87 | font-size: 1.25rem;
|
88 | margin-bottom: 0.5rem;
|
89 | }
|
90 | .box-content {
|
91 | line-height: 1.5rem;
|
92 | }
|
93 |
|
94 | #loading-status {
|
95 | text-align: center;
|
96 | margin-top: 1rem;
|
97 | }
|
98 |
|
99 | .prompt,
|
100 | .confirmation {
|
101 | margin-bottom: 0.25rem;
|
102 | }
|
103 |
|
104 | .confirmation {
|
105 | font-style: italic;
|
106 | }
|
107 |
|
108 | .hidden {
|
109 | display: none;
|
110 | }
|
111 | </style>
|
112 | </head>
|
113 | <body>
|
114 | <div id="container" class="container" style="-webkit-user-select: none; -webkit-app-region: drag;">
|
115 | <div class="waiting-header">Waiting for React to connect…</div>
|
116 | <div class="boxes" style="-webkit-app-region: none;">
|
117 | <div class="box">
|
118 | <div class="box-header">React Native</div>
|
119 | <div class="box-content">
|
120 | Open the <a
|
121 | id="rn-help-link"
|
122 | class="link"
|
123 | target="_blank"
|
124 | rel="noopener noreferrer"
|
125 | href="https://reactnative.dev/docs/debugging#accessing-the-in-app-developer-menu"
|
126 | >in-app developer menu</a> to connect.
|
127 | </div>
|
128 | </div>
|
129 | <div class="box">
|
130 | <div class="box-header">React DOM</div>
|
131 | <div class="box-content">
|
132 | <div id="box-content-prompt" class="prompt">
|
133 | Add one of the following (click to copy):
|
134 | </div>
|
135 | <div id="box-content-confirmation" class="confirmation hidden">
|
136 | Copied to clipboard.
|
137 | </div>
|
138 | <span class="input" contenteditable="true" id="localhost"></span>
|
139 | <span class="input" contenteditable="true" id="byip"></span>
|
140 | to the top of the page you want to debug,
|
141 | <br />
|
142 | <strong>before</strong> importing React DOM.
|
143 | </div>
|
144 | </div>
|
145 | <div id="loading-status">Starting the server…</div>
|
146 | </div>
|
147 | </div>
|
148 | <script>
|
149 | const fs = require('fs');
|
150 | let options;
|
151 | let useHttps = false;
|
152 |
|
153 | try {
|
154 | if (process.env.KEY && process.env.CERT) {
|
155 | options = {
|
156 | key: fs.readFileSync(process.env.KEY),
|
157 | cert: fs.readFileSync(process.env.CERT)
|
158 | };
|
159 | useHttps = true;
|
160 | }
|
161 | } catch (err) {
|
162 | console.error('Failed to process SSL options - ', err);
|
163 | options = undefined;
|
164 | }
|
165 |
|
166 | const {clipboard} = require("electron");
|
167 | const host = process.env.HOST || 'localhost';
|
168 | const protocol = useHttps ? 'https' : 'http';
|
169 | const port = Number(process.env.PORT || 8097);
|
170 | const localIp = require("ip").address();
|
171 | const defaultPort = (port === 443 && useHttps) || (port === 80 && !useHttps);
|
172 | const server = defaultPort ? `${protocol}://${host}` : `${protocol}://${host}:${port}`;
|
173 | const serverIp = defaultPort ? `${protocol}://${localIp}` : `${protocol}://${localIp}:${port}`;
|
174 | const $ = document.querySelector.bind(document);
|
175 | const $promptDiv = $("#box-content-prompt");
|
176 | const $confirmationDiv = $("#box-content-confirmation");
|
177 |
|
178 | let timeoutID;
|
179 |
|
180 | function selectAllAndCopy(event) {
|
181 | const element = event.target;
|
182 | if (window.getSelection) {
|
183 | const selection = window.getSelection();
|
184 | const range = document.createRange();
|
185 | range.selectNodeContents(element);
|
186 | selection.removeAllRanges();
|
187 | selection.addRange(range);
|
188 | clipboard.writeText(event.target.textContent);
|
189 |
|
190 | $promptDiv.classList.add('hidden');
|
191 | $confirmationDiv.classList.remove('hidden');
|
192 |
|
193 | if (timeoutID) {
|
194 | clearTimeout(timeoutID);
|
195 | }
|
196 |
|
197 | timeoutID = setTimeout(() => {
|
198 | $promptDiv.classList.remove('hidden');
|
199 | $confirmationDiv.classList.add('hidden');
|
200 | }, 1000);
|
201 | }
|
202 | }
|
203 |
|
204 | const link = $('#rn-help-link');
|
205 | link.addEventListener('click', event => {
|
206 | event.preventDefault();
|
207 | require('electron').shell.openExternal(link.href);
|
208 | });
|
209 |
|
210 | const $localhost = $("#localhost");
|
211 | $localhost.innerText = `<script src="${server}"></` + 'script>';
|
212 | $localhost.addEventListener('click', selectAllAndCopy);
|
213 | $localhost.addEventListener('focus', selectAllAndCopy);
|
214 |
|
215 | const $byIp = $("#byip");
|
216 | $byIp.innerText = `<script src="${serverIp}"></` + 'script>';
|
217 | $byIp.addEventListener('click', selectAllAndCopy);
|
218 | $byIp.addEventListener('focus', selectAllAndCopy);
|
219 |
|
220 | let devtools;
|
221 | try {
|
222 | devtools = require("react-devtools-core/standalone").default;
|
223 | } catch (err) {
|
224 | alert(
|
225 | err.toString() +
|
226 | "\n\nDid you run `yarn` and `yarn run build` in packages/react-devtools-core?"
|
227 | );
|
228 | }
|
229 | window.devtools = devtools;
|
230 | window.server = devtools
|
231 | .setContentDOMNode(document.getElementById("container"))
|
232 | .setStatusListener(function(status) {
|
233 | const element = document.getElementById("loading-status");
|
234 | if (element) {
|
235 | element.innerText = status;
|
236 | }
|
237 | })
|
238 | .startServer(port, host, options);
|
239 | </script>
|
240 | </body>
|
241 | </html>
|