@beclab/olaresid 0.1.6 → 0.1.8
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.
- package/CLI.md +150 -33
- package/README.md +23 -13
- package/dist/business/index.d.ts +3 -3
- package/dist/business/index.d.ts.map +1 -1
- package/dist/business/index.js +31 -37
- package/dist/business/index.js.map +1 -1
- package/dist/cli.js +148 -38
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +65 -6
- package/dist/index.js.map +1 -1
- package/dist/tag/tuple.js +2 -2
- package/dist/tag/tuple.js.map +1 -1
- package/dist/utils/crypto-utils.d.ts +4 -32
- package/dist/utils/crypto-utils.d.ts.map +1 -1
- package/dist/utils/crypto-utils.js +28 -116
- package/dist/utils/crypto-utils.js.map +1 -1
- package/examples/crypto-utilities.ts +3 -3
- package/examples/ed25519-jwk.ts +1 -1
- package/examples/frontend-demo/package-lock.json +67 -1
- package/examples/frontend-demo/package.json +1 -0
- package/examples/frontend-demo/src/App.vue +291 -236
- package/examples/frontend-demo/src/main.ts +3 -0
- package/examples/frontend-demo/src/style.css +18 -0
- package/examples/frontend-demo/webpack.config.js +1 -0
- package/examples/generate-mnemonic.ts +3 -3
- package/examples/register-subdomain.ts +1 -1
- package/examples/transfer-domain.ts +1 -1
- package/package.json +2 -1
- package/src/business/index.ts +23 -34
- package/src/cli.ts +181 -38
- package/src/index.ts +71 -6
- package/src/tag/tuple.ts +2 -2
- package/src/utils/crypto-utils.ts +30 -133
- package/examples/encoding-utils.ts +0 -96
- package/examples/frontend-demo/.dockerignore +0 -40
|
@@ -54,211 +54,213 @@
|
|
|
54
54
|
</div>
|
|
55
55
|
</section>
|
|
56
56
|
|
|
57
|
-
<!-- Domain
|
|
58
|
-
<section class="card">
|
|
59
|
-
<h2
|
|
57
|
+
<!-- Domain Input - Top Level -->
|
|
58
|
+
<section class="card domain-input-section">
|
|
59
|
+
<h2>🏷️ Domain Configuration</h2>
|
|
60
|
+
<p class="section-description">
|
|
61
|
+
Enter your domain name to manage its settings below
|
|
62
|
+
</p>
|
|
60
63
|
<div class="form-group">
|
|
64
|
+
<label class="input-label">Domain Name</label>
|
|
61
65
|
<input
|
|
62
66
|
v-model="domainName"
|
|
63
|
-
placeholder="e.g., 1.com"
|
|
67
|
+
placeholder="e.g., alice.olares.com or 1.com"
|
|
64
68
|
:disabled="!walletConnected"
|
|
65
|
-
class="input"
|
|
69
|
+
class="input input-large"
|
|
66
70
|
/>
|
|
67
71
|
</div>
|
|
68
|
-
<div class="
|
|
69
|
-
|
|
70
|
-
@click="getMetaInfo"
|
|
71
|
-
:disabled="
|
|
72
|
-
!domainName || !walletConnected || loading.metaInfo
|
|
73
|
-
"
|
|
74
|
-
class="btn-primary"
|
|
75
|
-
>
|
|
76
|
-
{{ loading.metaInfo ? 'Loading...' : 'Get Meta Info' }}
|
|
77
|
-
</button>
|
|
78
|
-
<button
|
|
79
|
-
@click="getOwner"
|
|
80
|
-
:disabled="
|
|
81
|
-
!domainName || !walletConnected || loading.owner
|
|
82
|
-
"
|
|
83
|
-
class="btn-primary"
|
|
84
|
-
>
|
|
85
|
-
{{ loading.owner ? 'Loading...' : 'Get Owner' }}
|
|
86
|
-
</button>
|
|
87
|
-
<button
|
|
88
|
-
@click="checkIsOwner"
|
|
89
|
-
:disabled="
|
|
90
|
-
!domainName || !walletConnected || loading.isOwner
|
|
91
|
-
"
|
|
92
|
-
class="btn-secondary"
|
|
93
|
-
>
|
|
94
|
-
{{ loading.isOwner ? 'Loading...' : 'Is Owner?' }}
|
|
95
|
-
</button>
|
|
96
|
-
</div>
|
|
97
|
-
<div v-if="results.domainContext" class="result">
|
|
98
|
-
<pre>{{ results.domainContext }}</pre>
|
|
72
|
+
<div v-if="!domainName && walletConnected" class="info-message">
|
|
73
|
+
👆 Please enter a domain name to access management features
|
|
99
74
|
</div>
|
|
100
75
|
</section>
|
|
101
76
|
|
|
102
|
-
<!--
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
<
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
</
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
</
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
<button
|
|
171
|
-
@click="generateRSAKeyPair"
|
|
172
|
-
:disabled="loading.generateRSA"
|
|
173
|
-
class="btn-purple"
|
|
174
|
-
>
|
|
175
|
-
{{
|
|
176
|
-
loading.generateRSA
|
|
177
|
-
? 'Generating...'
|
|
178
|
-
: '🔑 Generate RSA Key Pair'
|
|
179
|
-
}}
|
|
180
|
-
</button>
|
|
181
|
-
</div>
|
|
182
|
-
|
|
183
|
-
<div v-if="rsaKeyPair" class="rsa-keys">
|
|
184
|
-
<div class="key-display">
|
|
185
|
-
<strong>Public Key (PEM):</strong>
|
|
186
|
-
<pre
|
|
187
|
-
>{{
|
|
188
|
-
rsaKeyPair.publicKey.substring(0, 100)
|
|
189
|
-
}}...</pre
|
|
77
|
+
<!-- Domain Management Area - Only show when domain is entered -->
|
|
78
|
+
<div v-if="domainName" class="management-area">
|
|
79
|
+
<!-- Domain Info Demo -->
|
|
80
|
+
<section class="card subsection">
|
|
81
|
+
<h3>📋 Domain Info</h3>
|
|
82
|
+
<div class="button-grid">
|
|
83
|
+
<button
|
|
84
|
+
@click="getMetaInfo"
|
|
85
|
+
:disabled="
|
|
86
|
+
!domainName ||
|
|
87
|
+
!walletConnected ||
|
|
88
|
+
loading.metaInfo
|
|
89
|
+
"
|
|
90
|
+
class="btn-primary"
|
|
91
|
+
>
|
|
92
|
+
{{
|
|
93
|
+
loading.metaInfo
|
|
94
|
+
? 'Loading...'
|
|
95
|
+
: 'Get Meta Info'
|
|
96
|
+
}}
|
|
97
|
+
</button>
|
|
98
|
+
<button
|
|
99
|
+
@click="getOwner"
|
|
100
|
+
:disabled="
|
|
101
|
+
!domainName || !walletConnected || loading.owner
|
|
102
|
+
"
|
|
103
|
+
class="btn-primary"
|
|
104
|
+
>
|
|
105
|
+
{{ loading.owner ? 'Loading...' : 'Get Owner' }}
|
|
106
|
+
</button>
|
|
107
|
+
<button
|
|
108
|
+
@click="checkIsOwner"
|
|
109
|
+
:disabled="
|
|
110
|
+
!domainName ||
|
|
111
|
+
!walletConnected ||
|
|
112
|
+
loading.isOwner
|
|
113
|
+
"
|
|
114
|
+
class="btn-secondary"
|
|
115
|
+
>
|
|
116
|
+
{{ loading.isOwner ? 'Loading...' : 'Is Owner?' }}
|
|
117
|
+
</button>
|
|
118
|
+
</div>
|
|
119
|
+
<div v-if="results.domainContext" class="result">
|
|
120
|
+
<pre>{{ results.domainContext }}</pre>
|
|
121
|
+
</div>
|
|
122
|
+
</section>
|
|
123
|
+
|
|
124
|
+
<!-- IP Address Management -->
|
|
125
|
+
<section class="card subsection">
|
|
126
|
+
<h3>🌐 IP Address Management</h3>
|
|
127
|
+
<div class="form-group">
|
|
128
|
+
<input
|
|
129
|
+
v-model="ipAddress"
|
|
130
|
+
placeholder="e.g., 192.168.1.100"
|
|
131
|
+
:disabled="!walletConnected"
|
|
132
|
+
class="input"
|
|
133
|
+
/>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="button-grid">
|
|
136
|
+
<button
|
|
137
|
+
@click="setIP"
|
|
138
|
+
:disabled="
|
|
139
|
+
!domainName ||
|
|
140
|
+
!ipAddress ||
|
|
141
|
+
!walletConnected ||
|
|
142
|
+
loading.setIP
|
|
143
|
+
"
|
|
144
|
+
class="btn-success"
|
|
190
145
|
>
|
|
146
|
+
{{ loading.setIP ? 'Setting...' : 'Set IP' }}
|
|
147
|
+
</button>
|
|
148
|
+
<button
|
|
149
|
+
@click="getIP"
|
|
150
|
+
:disabled="
|
|
151
|
+
!domainName || !walletConnected || loading.getIP
|
|
152
|
+
"
|
|
153
|
+
class="btn-primary"
|
|
154
|
+
>
|
|
155
|
+
{{ loading.getIP ? 'Loading...' : 'Get IP' }}
|
|
156
|
+
</button>
|
|
157
|
+
<button
|
|
158
|
+
@click="removeIP"
|
|
159
|
+
:disabled="
|
|
160
|
+
!domainName ||
|
|
161
|
+
!walletConnected ||
|
|
162
|
+
loading.removeIP
|
|
163
|
+
"
|
|
164
|
+
class="btn-danger"
|
|
165
|
+
>
|
|
166
|
+
{{ loading.removeIP ? 'Removing...' : 'Remove IP' }}
|
|
167
|
+
</button>
|
|
168
|
+
</div>
|
|
169
|
+
<div v-if="results.ip" class="result success">
|
|
170
|
+
<pre>{{ results.ip }}</pre>
|
|
191
171
|
</div>
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
172
|
+
</section>
|
|
173
|
+
|
|
174
|
+
<!-- RSA Key Management -->
|
|
175
|
+
<section class="card subsection">
|
|
176
|
+
<h3>🔐 RSA Key Management</h3>
|
|
177
|
+
<div class="button-grid">
|
|
178
|
+
<button
|
|
179
|
+
@click="generateRSAKeyPair"
|
|
180
|
+
:disabled="loading.generateRSA"
|
|
181
|
+
class="btn-purple"
|
|
198
182
|
>
|
|
183
|
+
{{
|
|
184
|
+
loading.generateRSA
|
|
185
|
+
? 'Generating...'
|
|
186
|
+
: '🔑 Generate RSA Key Pair'
|
|
187
|
+
}}
|
|
188
|
+
</button>
|
|
199
189
|
</div>
|
|
200
|
-
</div>
|
|
201
190
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
class="
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
!domainName || !walletConnected || loading.getRSA
|
|
221
|
-
"
|
|
222
|
-
class="btn-primary"
|
|
223
|
-
>
|
|
224
|
-
{{
|
|
225
|
-
loading.getRSA ? 'Loading...' : 'Get RSA Public Key'
|
|
226
|
-
}}
|
|
227
|
-
</button>
|
|
228
|
-
<button
|
|
229
|
-
@click="removeRSAPublicKey"
|
|
230
|
-
:disabled="
|
|
231
|
-
!domainName || !walletConnected || loading.removeRSA
|
|
232
|
-
"
|
|
233
|
-
class="btn-danger"
|
|
234
|
-
>
|
|
235
|
-
{{
|
|
236
|
-
loading.removeRSA ? 'Removing...' : 'Remove RSA Key'
|
|
237
|
-
}}
|
|
238
|
-
</button>
|
|
239
|
-
</div>
|
|
191
|
+
<div v-if="rsaKeyPair" class="rsa-keys">
|
|
192
|
+
<div class="key-display">
|
|
193
|
+
<strong>Public Key (PEM):</strong>
|
|
194
|
+
<pre
|
|
195
|
+
>{{
|
|
196
|
+
rsaKeyPair.publicKey.substring(0, 100)
|
|
197
|
+
}}...</pre
|
|
198
|
+
>
|
|
199
|
+
</div>
|
|
200
|
+
<div class="key-display">
|
|
201
|
+
<strong>Private Key (PEM):</strong>
|
|
202
|
+
<pre
|
|
203
|
+
>{{
|
|
204
|
+
rsaKeyPair.privateKey.substring(0, 100)
|
|
205
|
+
}}...</pre
|
|
206
|
+
>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
240
209
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
210
|
+
<div class="button-grid">
|
|
211
|
+
<button
|
|
212
|
+
@click="setRSAPublicKey"
|
|
213
|
+
:disabled="
|
|
214
|
+
!domainName ||
|
|
215
|
+
!rsaKeyPair ||
|
|
216
|
+
!walletConnected ||
|
|
217
|
+
loading.setRSA
|
|
218
|
+
"
|
|
219
|
+
class="btn-success"
|
|
220
|
+
>
|
|
221
|
+
{{
|
|
222
|
+
loading.setRSA
|
|
223
|
+
? 'Setting...'
|
|
224
|
+
: 'Set RSA Public Key'
|
|
225
|
+
}}
|
|
226
|
+
</button>
|
|
227
|
+
<button
|
|
228
|
+
@click="getRSAPublicKey"
|
|
229
|
+
:disabled="
|
|
230
|
+
!domainName ||
|
|
231
|
+
!walletConnected ||
|
|
232
|
+
loading.getRSA
|
|
233
|
+
"
|
|
234
|
+
class="btn-primary"
|
|
235
|
+
>
|
|
236
|
+
{{
|
|
237
|
+
loading.getRSA
|
|
238
|
+
? 'Loading...'
|
|
239
|
+
: 'Get RSA Public Key'
|
|
240
|
+
}}
|
|
241
|
+
</button>
|
|
242
|
+
<button
|
|
243
|
+
@click="removeRSAPublicKey"
|
|
244
|
+
:disabled="
|
|
245
|
+
!domainName ||
|
|
246
|
+
!walletConnected ||
|
|
247
|
+
loading.removeRSA
|
|
248
|
+
"
|
|
249
|
+
class="btn-danger"
|
|
250
|
+
>
|
|
251
|
+
{{
|
|
252
|
+
loading.removeRSA
|
|
253
|
+
? 'Removing...'
|
|
254
|
+
: 'Remove RSA Key'
|
|
255
|
+
}}
|
|
256
|
+
</button>
|
|
257
|
+
</div>
|
|
257
258
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
259
|
+
<div v-if="results.rsa" class="result purple">
|
|
260
|
+
<pre>{{ results.rsa }}</pre>
|
|
261
|
+
</div>
|
|
262
|
+
</section>
|
|
263
|
+
</div>
|
|
262
264
|
|
|
263
265
|
<!-- Toast Messages -->
|
|
264
266
|
<div v-if="toast.show" class="toast" :class="toast.type">
|
|
@@ -274,8 +276,7 @@ import * as OlaresIDModule from '@beclab/olaresid';
|
|
|
274
276
|
import { ethers } from 'ethers';
|
|
275
277
|
|
|
276
278
|
const OlaresID = (OlaresIDModule as any).default || OlaresIDModule;
|
|
277
|
-
const { createRsaKeyPair
|
|
278
|
-
OlaresIDModule as any;
|
|
279
|
+
const { createRsaKeyPair } = OlaresIDModule as any;
|
|
279
280
|
|
|
280
281
|
declare global {
|
|
281
282
|
interface Window {
|
|
@@ -355,7 +356,6 @@ const handleChainChanged = async (chainId: string) => {
|
|
|
355
356
|
|
|
356
357
|
if (!isSupportedNetwork(chainId)) {
|
|
357
358
|
networkClass.value = 'unsupported';
|
|
358
|
-
// 切换到不支持的网络时清除 signer
|
|
359
359
|
if (walletConnected.value) {
|
|
360
360
|
olaresId.clearSigner();
|
|
361
361
|
}
|
|
@@ -416,7 +416,6 @@ const reinitializeOlaresId = async () => {
|
|
|
416
416
|
networkClass.value = 'testnet';
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
-
// 重新设置 signer(如果钱包已连接)
|
|
420
419
|
if (walletConnected.value && walletAddress.value) {
|
|
421
420
|
await setSignerToOlaresId();
|
|
422
421
|
console.log(
|
|
@@ -534,7 +533,6 @@ const switchToMainnet = async () => {
|
|
|
534
533
|
});
|
|
535
534
|
} catch (error: any) {
|
|
536
535
|
if (error.code === 4902) {
|
|
537
|
-
// 网络不存在,添加网络
|
|
538
536
|
try {
|
|
539
537
|
await window.ethereum.request({
|
|
540
538
|
method: 'wallet_addEthereumChain',
|
|
@@ -599,7 +597,7 @@ const switchToTestnet = async () => {
|
|
|
599
597
|
}
|
|
600
598
|
};
|
|
601
599
|
|
|
602
|
-
// Domain
|
|
600
|
+
// Domain Info Methods
|
|
603
601
|
const getMetaInfo = async () => {
|
|
604
602
|
loading.metaInfo = true;
|
|
605
603
|
results.domainContext = '';
|
|
@@ -709,26 +707,6 @@ const removeIP = async () => {
|
|
|
709
707
|
}
|
|
710
708
|
};
|
|
711
709
|
|
|
712
|
-
const convertIPv4ToBytes4 = () => {
|
|
713
|
-
try {
|
|
714
|
-
const bytes4 = ipv4ToBytes4(ipAddress.value);
|
|
715
|
-
results.ip = `IPv4 to Bytes4:\n${ipAddress.value} → ${bytes4}`;
|
|
716
|
-
showToast('Converted!', 'success');
|
|
717
|
-
} catch (err: any) {
|
|
718
|
-
showToast('Invalid IP address', 'error');
|
|
719
|
-
}
|
|
720
|
-
};
|
|
721
|
-
|
|
722
|
-
const convertBytes4ToIPv4 = () => {
|
|
723
|
-
try {
|
|
724
|
-
const ip = bytes4ToIpv4(ipAddress.value);
|
|
725
|
-
results.ip = `Bytes4 to IPv4:\n${ipAddress.value} → ${ip}`;
|
|
726
|
-
showToast('Converted!', 'success');
|
|
727
|
-
} catch (err: any) {
|
|
728
|
-
showToast('Invalid bytes4 format', 'error');
|
|
729
|
-
}
|
|
730
|
-
};
|
|
731
|
-
|
|
732
710
|
// RSA Methods
|
|
733
711
|
const generateRSAKeyPair = async () => {
|
|
734
712
|
loading.generateRSA = true;
|
|
@@ -804,27 +782,6 @@ const removeRSAPublicKey = async () => {
|
|
|
804
782
|
loading.removeRSA = false;
|
|
805
783
|
}
|
|
806
784
|
};
|
|
807
|
-
|
|
808
|
-
const convertPEMToDER = () => {
|
|
809
|
-
try {
|
|
810
|
-
const der = pemToDer(rsaKeyPair.value.publicKey);
|
|
811
|
-
results.rsa = `PEM to DER:\n${der.substring(0, 100)}...`;
|
|
812
|
-
showToast('Converted to DER!', 'success');
|
|
813
|
-
} catch (err: any) {
|
|
814
|
-
showToast('Conversion failed', 'error');
|
|
815
|
-
}
|
|
816
|
-
};
|
|
817
|
-
|
|
818
|
-
const convertDERToPEM = () => {
|
|
819
|
-
try {
|
|
820
|
-
const der = pemToDer(rsaKeyPair.value.publicKey);
|
|
821
|
-
const pem = derToPem(der);
|
|
822
|
-
results.rsa = `DER to PEM:\n${pem}`;
|
|
823
|
-
showToast('Converted to PEM!', 'success');
|
|
824
|
-
} catch (err: any) {
|
|
825
|
-
showToast('Conversion failed', 'error');
|
|
826
|
-
}
|
|
827
|
-
};
|
|
828
785
|
</script>
|
|
829
786
|
|
|
830
787
|
<style scoped>
|
|
@@ -946,6 +903,102 @@ main {
|
|
|
946
903
|
cursor: not-allowed;
|
|
947
904
|
}
|
|
948
905
|
|
|
906
|
+
/* Domain Input Section - Top Level */
|
|
907
|
+
.domain-input-section {
|
|
908
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
909
|
+
color: white;
|
|
910
|
+
border: 3px solid #667eea;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
.domain-input-section h2 {
|
|
914
|
+
color: white;
|
|
915
|
+
margin-bottom: 0.5rem;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
.section-description {
|
|
919
|
+
color: rgba(255, 255, 255, 0.9);
|
|
920
|
+
font-size: 0.95rem;
|
|
921
|
+
margin-bottom: 1rem;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
.input-label {
|
|
925
|
+
display: block;
|
|
926
|
+
font-weight: 600;
|
|
927
|
+
margin-bottom: 0.5rem;
|
|
928
|
+
color: white;
|
|
929
|
+
font-size: 1rem;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
.input-large {
|
|
933
|
+
font-size: 1.1rem;
|
|
934
|
+
padding: 1rem;
|
|
935
|
+
background: white;
|
|
936
|
+
color: #333;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
.info-message {
|
|
940
|
+
background: rgba(255, 255, 255, 0.2);
|
|
941
|
+
padding: 0.75rem;
|
|
942
|
+
border-radius: 8px;
|
|
943
|
+
margin-top: 0.75rem;
|
|
944
|
+
text-align: center;
|
|
945
|
+
font-size: 0.95rem;
|
|
946
|
+
color: white;
|
|
947
|
+
border: 1px dashed rgba(255, 255, 255, 0.5);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
/* Management Area - Container for subsections */
|
|
951
|
+
.management-area {
|
|
952
|
+
margin-top: 2rem;
|
|
953
|
+
padding-left: 1.5rem;
|
|
954
|
+
border-left: 4px solid #667eea;
|
|
955
|
+
position: relative;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
.management-area::before {
|
|
959
|
+
content: '';
|
|
960
|
+
position: absolute;
|
|
961
|
+
left: -2px;
|
|
962
|
+
top: 0;
|
|
963
|
+
bottom: 0;
|
|
964
|
+
width: 4px;
|
|
965
|
+
background: linear-gradient(180deg, #667eea 0%, transparent 100%);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/* Subsection - Child cards */
|
|
969
|
+
.subsection {
|
|
970
|
+
position: relative;
|
|
971
|
+
margin-bottom: 1.5rem;
|
|
972
|
+
border-left: 3px solid #e0e0e0;
|
|
973
|
+
padding-left: 1.5rem;
|
|
974
|
+
margin-left: -1.5rem;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
.subsection::before {
|
|
978
|
+
content: '';
|
|
979
|
+
position: absolute;
|
|
980
|
+
left: -1.5rem;
|
|
981
|
+
top: 2rem;
|
|
982
|
+
width: 1rem;
|
|
983
|
+
height: 2px;
|
|
984
|
+
background: #e0e0e0;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
.subsection h3 {
|
|
988
|
+
font-size: 1.25rem;
|
|
989
|
+
color: #555;
|
|
990
|
+
margin-bottom: 1rem;
|
|
991
|
+
font-weight: 600;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
.subsection:hover {
|
|
995
|
+
border-left-color: #667eea;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
.subsection:hover::before {
|
|
999
|
+
background: #667eea;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
949
1002
|
.button-grid {
|
|
950
1003
|
display: grid;
|
|
951
1004
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
@@ -968,6 +1021,8 @@ button {
|
|
|
968
1021
|
font-weight: 600;
|
|
969
1022
|
cursor: pointer;
|
|
970
1023
|
transition: all 0.3s;
|
|
1024
|
+
min-width: 150px;
|
|
1025
|
+
width: 100%;
|
|
971
1026
|
}
|
|
972
1027
|
|
|
973
1028
|
button:disabled {
|
|
@@ -11,11 +11,13 @@ body {
|
|
|
11
11
|
line-height: 1.6;
|
|
12
12
|
color: #333;
|
|
13
13
|
background: #f5f5f5;
|
|
14
|
+
overflow-x: hidden;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
/* App layout */
|
|
17
18
|
.app {
|
|
18
19
|
max-width: 900px;
|
|
20
|
+
width: 100%;
|
|
19
21
|
margin: 0 auto;
|
|
20
22
|
padding: 20px;
|
|
21
23
|
min-height: 100vh;
|
|
@@ -116,6 +118,8 @@ button:disabled {
|
|
|
116
118
|
/* Main content */
|
|
117
119
|
main {
|
|
118
120
|
flex: 1;
|
|
121
|
+
width: 100%;
|
|
122
|
+
overflow-x: hidden;
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
.section {
|
|
@@ -123,6 +127,8 @@ main {
|
|
|
123
127
|
padding: 30px;
|
|
124
128
|
border-radius: 8px;
|
|
125
129
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
130
|
+
width: 100%;
|
|
131
|
+
overflow: hidden;
|
|
126
132
|
}
|
|
127
133
|
|
|
128
134
|
.section h2 {
|
|
@@ -170,6 +176,9 @@ input:disabled {
|
|
|
170
176
|
background: #f8f9fa;
|
|
171
177
|
border-radius: 6px;
|
|
172
178
|
border-left: 4px solid #3498db;
|
|
179
|
+
width: 100%;
|
|
180
|
+
overflow: hidden;
|
|
181
|
+
word-wrap: break-word;
|
|
173
182
|
}
|
|
174
183
|
|
|
175
184
|
.result h3 {
|
|
@@ -216,6 +225,8 @@ input:disabled {
|
|
|
216
225
|
display: flex;
|
|
217
226
|
gap: 10px;
|
|
218
227
|
align-items: center;
|
|
228
|
+
width: 100%;
|
|
229
|
+
min-width: 0;
|
|
219
230
|
}
|
|
220
231
|
|
|
221
232
|
code {
|
|
@@ -228,7 +239,10 @@ code {
|
|
|
228
239
|
font-family: 'Monaco', 'Courier New', monospace;
|
|
229
240
|
font-size: 0.85rem;
|
|
230
241
|
word-break: break-all;
|
|
242
|
+
overflow-wrap: break-word;
|
|
231
243
|
color: #333;
|
|
244
|
+
max-width: 100%;
|
|
245
|
+
overflow-x: auto;
|
|
232
246
|
}
|
|
233
247
|
|
|
234
248
|
pre {
|
|
@@ -242,6 +256,10 @@ pre {
|
|
|
242
256
|
max-height: 500px;
|
|
243
257
|
overflow-y: auto;
|
|
244
258
|
border: 1px solid #ddd;
|
|
259
|
+
word-break: break-all;
|
|
260
|
+
overflow-wrap: break-word;
|
|
261
|
+
white-space: pre-wrap;
|
|
262
|
+
max-width: 100%;
|
|
245
263
|
}
|
|
246
264
|
|
|
247
265
|
/* Error display */
|