dsh-agent-office 4.6.1 → 4.6.2
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/lib/client.js +87 -25
- package/lib/index.js +16 -1
- package/lib/panel.html +152 -152
- package/lib/state.js +34 -0
- package/package.json +1 -1
- package/panel-src/assets/exp-uneven.png +0 -0
- package/panel-src/main.js +3 -1
package/lib/client.js
CHANGED
|
@@ -76,6 +76,8 @@ window.__ModuleLoader__.load({
|
|
|
76
76
|
} catch { return false }
|
|
77
77
|
})
|
|
78
78
|
const [weatherBusy, setWeatherBusy] = useState(false)
|
|
79
|
+
const [city, setCity] = useState('')
|
|
80
|
+
const [cityBusy, setCityBusy] = useState(false)
|
|
79
81
|
|
|
80
82
|
const choose = async event => {
|
|
81
83
|
const file = event.target.files?.[0]
|
|
@@ -118,33 +120,74 @@ window.__ModuleLoader__.load({
|
|
|
118
120
|
|
|
119
121
|
const askWeather = () => {
|
|
120
122
|
if (!navigator.geolocation) {
|
|
121
|
-
setMessage('
|
|
123
|
+
setMessage('当前环境不支持系统定位,请在下面手动输入城市')
|
|
122
124
|
return
|
|
123
125
|
}
|
|
124
126
|
setWeatherBusy(true)
|
|
125
|
-
setMessage('
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
127
|
+
setMessage('正在请求定位权限;如果没有弹窗,请在下面手动输入城市…')
|
|
128
|
+
let settled = false
|
|
129
|
+
const finish = (ok, text) => {
|
|
130
|
+
if (settled) return
|
|
131
|
+
settled = true
|
|
132
|
+
clearTimeout(timer)
|
|
133
|
+
setWeatherBusy(false)
|
|
134
|
+
setWeatherOn(ok)
|
|
135
|
+
setMessage(text)
|
|
136
|
+
}
|
|
137
|
+
const timer = setTimeout(() => {
|
|
138
|
+
finish(false, '系统定位没有响应。请在下面手动输入城市,例如:上海')
|
|
139
|
+
}, 12000)
|
|
140
|
+
try {
|
|
141
|
+
navigator.geolocation.getCurrentPosition(
|
|
142
|
+
position => {
|
|
143
|
+
try {
|
|
144
|
+
localStorage.setItem(WEATHER_KEY, JSON.stringify({
|
|
145
|
+
lat: position.coords.latitude,
|
|
146
|
+
lon: position.coords.longitude,
|
|
147
|
+
at: Date.now(),
|
|
148
|
+
}))
|
|
149
|
+
finish(true, '已开启本地天气,面板会按所在城市显示天气效果')
|
|
150
|
+
} catch {
|
|
151
|
+
finish(false, '位置保存失败')
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
error => finish(false, '没有拿到定位权限:' + String(error?.message ?? '未授权') + '。可以改用手动输入城市。'),
|
|
155
|
+
{ enableHighAccuracy: false, timeout: 10000, maximumAge: 10 * 60 * 1000 },
|
|
156
|
+
)
|
|
157
|
+
} catch (error) {
|
|
158
|
+
finish(false, '定位启动失败:' + String(error?.message ?? error) + '。请手动输入城市。')
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const useCity = async () => {
|
|
163
|
+
const name = city.trim()
|
|
164
|
+
if (!name) {
|
|
165
|
+
setMessage('先输入城市名,例如:上海')
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
setCityBusy(true)
|
|
169
|
+
setMessage('正在查找「' + name + '」…')
|
|
170
|
+
try {
|
|
171
|
+
const response = await fetch('/agent-office/api/geocode?name=' + encodeURIComponent(name), { cache: 'no-store' })
|
|
172
|
+
const data = await response.json()
|
|
173
|
+
if (!data?.ok) {
|
|
174
|
+
setMessage(data?.error ? String(data.error) : '没有找到这个城市')
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
localStorage.setItem(WEATHER_KEY, JSON.stringify({
|
|
178
|
+
lat: data.lat,
|
|
179
|
+
lon: data.lon,
|
|
180
|
+
name: [data.name, data.admin1].filter(Boolean).join(' · '),
|
|
181
|
+
at: Date.now(),
|
|
182
|
+
}))
|
|
183
|
+
setWeatherOn(true)
|
|
184
|
+
setMessage('已使用「' + [data.name, data.admin1].filter(Boolean).join(' · ') + '」的天气')
|
|
185
|
+
setCity('')
|
|
186
|
+
} catch (error) {
|
|
187
|
+
setMessage('城市查询失败:' + String(error?.message ?? error))
|
|
188
|
+
} finally {
|
|
189
|
+
setCityBusy(false)
|
|
190
|
+
}
|
|
148
191
|
}
|
|
149
192
|
|
|
150
193
|
const closeWeather = () => {
|
|
@@ -210,7 +253,26 @@ window.__ModuleLoader__.load({
|
|
|
210
253
|
children: weatherBusy ? '定位中…' : weatherOn ? '关闭天气' : '允许定位',
|
|
211
254
|
}),
|
|
212
255
|
] }),
|
|
213
|
-
jsx('p', { style: { ...textStyle, margin: '8px 0 0', fontSize: 12 }, children: weatherOn ? '
|
|
256
|
+
jsx('p', { style: { ...textStyle, margin: '8px 0 0', fontSize: 12 }, children: weatherOn ? '天气已开启。面板会按所在城市显示晴天、云、雨、雪或风;位置只保存在本地。' : '允许定位后,面板会获取所在城市天气;如果系统定位没有反应,可以在下面直接输入城市。' }),
|
|
257
|
+
jsx('div', { style: { display: 'flex', gap: 8, marginTop: 10 }, children: [
|
|
258
|
+
jsx('input', {
|
|
259
|
+
type: 'text',
|
|
260
|
+
value: city,
|
|
261
|
+
placeholder: '手动输入城市,例如:上海',
|
|
262
|
+
onChange: event => setCity(event.target.value),
|
|
263
|
+
onKeyDown: event => { if (event.key === 'Enter') useCity() },
|
|
264
|
+
style: { flex: '1 1 0', minWidth: 0, height: 30, padding: '0 10px', borderRadius: 8, border: '1px solid rgba(127,127,127,.35)', background: 'transparent', color: 'inherit', fontSize: 13, outline: 'none' },
|
|
265
|
+
}),
|
|
266
|
+
jsx('button', {
|
|
267
|
+
type: 'button',
|
|
268
|
+
className: 'ns-setting-btn',
|
|
269
|
+
style: { ...buttonStyle, height: 30, padding: '0 12px', flex: 'none' },
|
|
270
|
+
onClick: useCity,
|
|
271
|
+
disabled: cityBusy,
|
|
272
|
+
children: cityBusy ? '查找中…' : '使用城市',
|
|
273
|
+
}),
|
|
274
|
+
] }),
|
|
275
|
+
jsx('p', { style: { ...textStyle, margin: '8px 0 0', fontSize: 11 }, children: '系统定位超时或没有弹窗时,直接输入城市即可。位置只保存在本地。' }),
|
|
214
276
|
],
|
|
215
277
|
}),
|
|
216
278
|
message ? jsx('p', { style: { ...textStyle, marginTop: 10, marginBottom: 0 }, children: message }) : null,
|
package/lib/index.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 不依赖 ntfy、不依赖外部进程;面板与 DSH 同源,直接轮询本机 API。
|
|
9
9
|
*/
|
|
10
10
|
import { readFile } from 'node:fs/promises'
|
|
11
|
-
import { createNightShiftStore, fetchDeepSeekBalance, fetchWeather } from './state.js'
|
|
11
|
+
import { createNightShiftStore, fetchDeepSeekBalance, fetchWeather, fetchGeocode } from './state.js'
|
|
12
12
|
|
|
13
13
|
export const name = 'agent-office'
|
|
14
14
|
export const inject = ['webServer']
|
|
@@ -71,6 +71,21 @@ export function apply(ctx, config) {
|
|
|
71
71
|
},
|
|
72
72
|
}), 'agent-office: api')
|
|
73
73
|
|
|
74
|
+
ctx.effect(() => ctx.webServer.register({
|
|
75
|
+
kind: 'exact',
|
|
76
|
+
path: '/agent-office/api/geocode',
|
|
77
|
+
handler: async (req, res) => {
|
|
78
|
+
if (!hostTrusted(req)) return json(res, { ok: false, error: '不被信任的 Host' }, 403)
|
|
79
|
+
try {
|
|
80
|
+
const url = new URL(req.url ?? '/', 'http://localhost')
|
|
81
|
+
return json(res, await fetchGeocode({ name: url.searchParams.get('name') }))
|
|
82
|
+
} catch (error) {
|
|
83
|
+
ctx.logger.error(error instanceof Error ? error : new Error(String(error)))
|
|
84
|
+
return json(res, { ok: false, error: '城市查询暂时不可用' }, 500)
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
}), 'agent-office: geocode')
|
|
88
|
+
|
|
74
89
|
ctx.effect(() => ctx.webServer.register({
|
|
75
90
|
kind: 'exact',
|
|
76
91
|
path: '/agent-office/api/weather',
|