@8wave/ai-elements 0.119.0-beta.20 → 0.119.0-beta.26
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/dist/_chunks/PkToolShowWeather-DfLsFayn.js +340 -0
- package/dist/_chunks/PkToolShowWeather-DfLsFayn.js.map +1 -0
- package/dist/ai-elements.es.js +37 -23
- package/dist/ai-elements.es.js.map +1 -1
- package/dist-vue/PkChatbot.js +1 -1
- package/dist-vue/PkChatbotMessages.js +1 -1
- package/dist-vue/PkChatbotViewChat.js +1 -1
- package/dist-vue/_chunks/{PkChatbot-CM9ST43I.js → PkChatbot-536vsOfp.js} +2 -2
- package/dist-vue/_chunks/{PkChatbot-CM9ST43I.js.map → PkChatbot-536vsOfp.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotMessages-D9uf0NMI.js → PkChatbotMessages-CnI3e9wF.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotMessages-D9uf0NMI.js.map → PkChatbotMessages-CnI3e9wF.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewChat-B_7IdZIz.js → PkChatbotViewChat-CVT6OUIv.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotViewChat-B_7IdZIz.js.map → PkChatbotViewChat-CVT6OUIv.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowDiagram-BJ_ryTsD.js → PkToolShowDiagram-7WxDObZ6.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowDiagram-BJ_ryTsD.js.map → PkToolShowDiagram-7WxDObZ6.js.map} +1 -1
- package/dist-vue/_chunks/PkToolShowWeather-BOuNrvY2.js +339 -0
- package/dist-vue/_chunks/PkToolShowWeather-BOuNrvY2.js.map +1 -0
- package/dist-vue/_chunks/createChatbotApiClient-DSLxswr4.js.map +1 -1
- package/dist-vue/composables.js +36 -22
- package/dist-vue/composables.js.map +1 -1
- package/dist-vue/index.js +250 -250
- package/dist-vue/index.js.map +1 -1
- package/dist-vue/locales.js +8 -6
- package/dist-vue/packages/ability/src/index.d.ts +4 -0
- package/dist-vue/packages/components/src/agentGlyphs.d.ts +5 -3
- package/dist-vue/packages/models/src/schema/AgentApiTool.d.ts +8 -0
- package/dist-vue/packages/models/src/schema/Invitation.d.ts +1 -1
- package/dist-vue/packages/models/src/schema/Weather.d.ts +7 -0
- package/package.json +1 -1
- package/dist/_chunks/PkToolShowWeather-BFOlS1AS.js +0 -280
- package/dist/_chunks/PkToolShowWeather-BFOlS1AS.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowWeather-BH-FtLpk.js +0 -279
- package/dist-vue/_chunks/PkToolShowWeather-BH-FtLpk.js.map +0 -1
package/dist-vue/locales.js
CHANGED
|
@@ -227,7 +227,7 @@ var e = {
|
|
|
227
227
|
messagePlaceholder: "Placeholder",
|
|
228
228
|
dismissableNotice: "Notice",
|
|
229
229
|
footer: "Footer",
|
|
230
|
-
privacyPolicyNotice: "
|
|
230
|
+
privacyPolicyNotice: "Privacy notice",
|
|
231
231
|
fileUpload: "File upload",
|
|
232
232
|
fileUploadEnabled: "Enable file upload",
|
|
233
233
|
fileUploadMaxFileSize: "Max file size (MB)",
|
|
@@ -460,8 +460,8 @@ var e = {
|
|
|
460
460
|
},
|
|
461
461
|
visibility: {
|
|
462
462
|
public: "Public",
|
|
463
|
-
private: "
|
|
464
|
-
organization: "Organization"
|
|
463
|
+
private: "Signed-in users",
|
|
464
|
+
organization: "Organization only"
|
|
465
465
|
},
|
|
466
466
|
searchMode: {
|
|
467
467
|
balanced: "Balanced (vector + keywords)",
|
|
@@ -612,6 +612,7 @@ var e = {
|
|
|
612
612
|
},
|
|
613
613
|
weather: {
|
|
614
614
|
today: "Today",
|
|
615
|
+
unavailable: "The weather forecast is not available.",
|
|
615
616
|
feelsLike: "Feels like",
|
|
616
617
|
dailyForecast: "Daily forecast",
|
|
617
618
|
airQuality: "Air quality"
|
|
@@ -1042,7 +1043,7 @@ var e = {
|
|
|
1042
1043
|
interactiveLogin: "Permette agli utenti anonimi di accedere dalla chat. L'external token resta sempre attivo.",
|
|
1043
1044
|
customCss: "CSS iniettato nello shadow DOM del widget. Permette di personalizzare l'interfaccia della chat dall'esterno.",
|
|
1044
1045
|
dismissableNotice: "Verrà nascosto dopo il primo messaggio.",
|
|
1045
|
-
privacyPolicyNotice: "
|
|
1046
|
+
privacyPolicyNotice: "Compare in fondo al modulo di contatto. Supporta il Markdown.",
|
|
1046
1047
|
fileUploadEnabled: "Permetti agli utenti di allegare file ai messaggi.",
|
|
1047
1048
|
approvalChannels: "Un tool può chiedere conferma all'utente prima di eseguire: espandi il tool per attivarla. Via API o in esecuzioni schedulate la chiamata viene rifiutata.",
|
|
1048
1049
|
platformSupported: "Resa nativa su WhatsApp e Teams",
|
|
@@ -1085,8 +1086,8 @@ var e = {
|
|
|
1085
1086
|
},
|
|
1086
1087
|
visibility: {
|
|
1087
1088
|
public: "Pubblico",
|
|
1088
|
-
private: "
|
|
1089
|
-
organization: "
|
|
1089
|
+
private: "Utenti registrati",
|
|
1090
|
+
organization: "Solo organizzazione"
|
|
1090
1091
|
},
|
|
1091
1092
|
searchMode: {
|
|
1092
1093
|
balanced: "Bilanciato (vettoriale + parole chiave)",
|
|
@@ -1237,6 +1238,7 @@ var e = {
|
|
|
1237
1238
|
},
|
|
1238
1239
|
weather: {
|
|
1239
1240
|
today: "Oggi",
|
|
1241
|
+
unavailable: "Le previsioni meteo non sono disponibili.",
|
|
1240
1242
|
feelsLike: "Percepita",
|
|
1241
1243
|
dailyForecast: "Previsioni giornaliere",
|
|
1242
1244
|
airQuality: "Qualità dell'aria"
|
|
@@ -31,6 +31,10 @@ export type PermissionMap = Partial<Record<Action, Subject>>;
|
|
|
31
31
|
* Evaluates a route-style permission map ({ action: Subject }) against an
|
|
32
32
|
* ability instance. When `payload` is provided it is bound as subject
|
|
33
33
|
* conditions, same as the HTTP route preHandler does with `request.body`.
|
|
34
|
+
* An empty object is checked like a missing payload: it says nothing about
|
|
35
|
+
* the resource, and as a subject it has no field for a rule's conditions to
|
|
36
|
+
* match, so a client that sent `{}` to a route taking no body (activate,
|
|
37
|
+
* deactivate) was refused what the same request without a body got.
|
|
34
38
|
* Returns the first denied pair, or null when every check passes.
|
|
35
39
|
* Single source of truth for both the HTTP preHandler and the Platform MCP
|
|
36
40
|
* tool handlers.
|
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
*
|
|
17
17
|
* Visibility used to be a padlock for all three values, and the open one sat
|
|
18
18
|
* on "public": that reads as "unprotected", which is both alarming and untrue.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* Each glyph now says who can use the agent: the globe for anyone, the
|
|
20
|
+
* shielded user for anyone signed in, the building for the organization.
|
|
21
|
+
* "private" kept a closed padlock for a while, but it is the wider of the two
|
|
22
|
+
* restricted values (any signed-in account, of any organization), and the
|
|
23
|
+
* padlock made it read as the narrowest.
|
|
22
24
|
*/
|
|
23
25
|
export declare const AGENT_STATUS_GLYPHS: Record<string, string>;
|
|
24
26
|
export declare const AGENT_VISIBILITY_GLYPHS: Record<string, string>;
|
|
@@ -85,6 +85,14 @@ export declare const AgentApiToolSchema: z.ZodObject<{
|
|
|
85
85
|
deletedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
86
86
|
deletedBy: z.ZodNullable<z.ZodString>;
|
|
87
87
|
}, z.core.$strip>;
|
|
88
|
+
/**
|
|
89
|
+
* Turns what is typed in the name field into the characters an API tool name
|
|
90
|
+
* allows: accents dropped, camelCase split with a hyphen, lowercase, every run
|
|
91
|
+
* of other characters a hyphen. Underscores and hyphens are kept, so a valid
|
|
92
|
+
* name comes back unchanged: the field applies this on every update, including
|
|
93
|
+
* the one the input emits when the dialog loads a stored tool.
|
|
94
|
+
*/
|
|
95
|
+
export declare function toApiToolName(value?: string): string;
|
|
88
96
|
export declare const AgentApiToolCreateSchema: z.ZodObject<{
|
|
89
97
|
name: z.ZodString;
|
|
90
98
|
description: z.ZodString;
|
|
@@ -27,4 +27,4 @@ export declare const InvitationsQuerystringSchema: z.ZodObject<{
|
|
|
27
27
|
'page[size]': z.ZodDefault<z.ZodPipe<z.ZodUnion<[z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
|
|
28
28
|
}, z.core.$strip>;
|
|
29
29
|
export type InvitationsQuerystring = z.infer<typeof InvitationsQuerystringSchema>;
|
|
30
|
-
export declare const getColorByInvitationStatus: (status: Invitation["status"]) => "success" | "danger" | "warning" | "info" | undefined;
|
|
30
|
+
export declare const getColorByInvitationStatus: (status: Invitation["status"]) => "success" | "danger" | "warning" | "info" | "gray" | undefined;
|
|
@@ -42,3 +42,10 @@ export declare const WeatherDataSchema: z.ZodObject<{
|
|
|
42
42
|
}, z.core.$strip>>;
|
|
43
43
|
}, z.core.$strip>;
|
|
44
44
|
export type WeatherData = z.infer<typeof WeatherDataSchema>;
|
|
45
|
+
/**
|
|
46
|
+
* Whether a value carries something the weather card can draw: current
|
|
47
|
+
* conditions, or at least one hourly or daily entry. `showWeather` takes only
|
|
48
|
+
* a `weatherKey`, so its input is a forecast only in conversations from before
|
|
49
|
+
* the key, and a call that failed (a mangled key, expired data) has no output.
|
|
50
|
+
*/
|
|
51
|
+
export declare function hasWeatherForecast(value: unknown): value is WeatherData;
|
package/package.json
CHANGED
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
import { $ as e, C as t, Ct as n, Dt as r, L as i, O as a, St as o, X as s, _ as c, b as l, d as u, g as ee, h as d, it as f, m as p, mt as m, r as h, t as g, v as _, wt as v, x as y, z as b } from "./vue.runtime.esm-bundler-DKVd2gci.js";
|
|
2
|
-
import { r as te } from "./vue-i18n-CooVjJjT.js";
|
|
3
|
-
import "./src-Dt4rhhPN.js";
|
|
4
|
-
import { n as x, t as ne } from "./index.es-dTcgyeM4.js";
|
|
5
|
-
//#region ../../packages/utils/src/weather.ts
|
|
6
|
-
function re(e) {
|
|
7
|
-
return e === void 0 ? "unknown" : e === 0 ? "clear" : e === 1 ? "mainlyClear" : e === 2 ? "partlyCloudy" : e === 3 ? "overcast" : e === 45 || e === 48 ? "fog" : e >= 51 && e <= 55 ? "drizzle" : e === 56 || e === 57 ? "freezingDrizzle" : e >= 61 && e <= 65 ? "rain" : e === 66 || e === 67 ? "freezingRain" : e >= 71 && e <= 77 ? "snow" : e >= 80 && e <= 82 ? "rainShowers" : e === 85 || e === 86 ? "snowShowers" : e === 95 ? "thunderstorm" : e === 96 || e === 99 ? "thunderstormHail" : "unknown";
|
|
8
|
-
}
|
|
9
|
-
function S(e) {
|
|
10
|
-
return e <= 20 ? {
|
|
11
|
-
index: 0,
|
|
12
|
-
key: "good"
|
|
13
|
-
} : e <= 40 ? {
|
|
14
|
-
index: 1,
|
|
15
|
-
key: "fair"
|
|
16
|
-
} : e <= 60 ? {
|
|
17
|
-
index: 2,
|
|
18
|
-
key: "moderate"
|
|
19
|
-
} : e <= 80 ? {
|
|
20
|
-
index: 3,
|
|
21
|
-
key: "poor"
|
|
22
|
-
} : e <= 100 ? {
|
|
23
|
-
index: 4,
|
|
24
|
-
key: "veryPoor"
|
|
25
|
-
} : {
|
|
26
|
-
index: 5,
|
|
27
|
-
key: "hazardous"
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
//#endregion
|
|
31
|
-
//#region ../../packages/components/src/chat/PkToolShowWeather.vue?vue&type=script&setup=true&lang.ts
|
|
32
|
-
a(), n(), f(), h();
|
|
33
|
-
var ie = {
|
|
34
|
-
key: 0,
|
|
35
|
-
class: "border border-surface-3 rounded-xl w-full overflow-hidden bg-surface"
|
|
36
|
-
}, ae = { class: "px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40" }, oe = { class: "font-bold truncate" }, se = { class: "ml-auto shrink-0 capitalize" }, ce = {
|
|
37
|
-
key: 0,
|
|
38
|
-
class: "px-sm py-md flex items-center gap-md flex-wrap"
|
|
39
|
-
}, le = { class: "flex items-center gap-8" }, C = { class: "text-48 font-light leading-none text-word-1" }, w = { class: "flex flex-col gap-4 text-12 text-word-3" }, T = { class: "flex items-center gap-6" }, E = { class: "flex items-center gap-6" }, D = { class: "flex items-center gap-6" }, O = {
|
|
40
|
-
key: 0,
|
|
41
|
-
class: "flex items-center gap-6"
|
|
42
|
-
}, k = {
|
|
43
|
-
key: 1,
|
|
44
|
-
class: "px-sm py-md flex items-center gap-md flex-wrap"
|
|
45
|
-
}, A = { class: "flex items-center gap-8" }, j = { class: "flex flex-col leading-none" }, M = { class: "text-48 font-light text-word-1" }, N = { class: "text-20 text-word-3" }, P = { class: "flex flex-col gap-4 text-12 text-word-3" }, F = {
|
|
46
|
-
key: 0,
|
|
47
|
-
class: "flex items-center gap-6"
|
|
48
|
-
}, I = {
|
|
49
|
-
key: 1,
|
|
50
|
-
class: "flex items-center gap-6"
|
|
51
|
-
}, L = { class: "flex items-center gap-6" }, R = {
|
|
52
|
-
key: 0,
|
|
53
|
-
class: "border-t border-surface-3"
|
|
54
|
-
}, z = { class: "flex overflow-x-auto gap-2 px-sm py-8 light-scrollbar" }, B = { class: "text-word-3" }, V = { class: "font-medium text-word-1" }, ue = {
|
|
55
|
-
key: 0,
|
|
56
|
-
class: "text-word-5 text-smaller"
|
|
57
|
-
}, H = ["onClick"], U = { class: "w-48 shrink-0 text-word-3 capitalize" }, de = {
|
|
58
|
-
key: 0,
|
|
59
|
-
class: "text-word-5 text-11 w-32 shrink-0"
|
|
60
|
-
}, fe = {
|
|
61
|
-
key: 1,
|
|
62
|
-
class: "w-32 shrink-0"
|
|
63
|
-
}, pe = { class: "ml-auto flex items-center gap-8 text-word-3" }, me = { class: "text-word-1 font-semibold" }, he = {
|
|
64
|
-
key: 2,
|
|
65
|
-
class: "border-t border-surface-3 px-sm py-8 flex items-center gap-8 text-12"
|
|
66
|
-
}, ge = { class: "text-word-3" }, W = /* @__PURE__ */ t({
|
|
67
|
-
__name: "PkToolShowWeather",
|
|
68
|
-
props: { part: { type: null } },
|
|
69
|
-
setup(t) {
|
|
70
|
-
let n = t, { t: a, d: f, n: h } = te({ useScope: "global" }), W = p(() => n.part), G = p(() => W.value.output ?? W.value.input ?? null), K = m(0), q = m(!G.value?.current);
|
|
71
|
-
s(K, (e) => {
|
|
72
|
-
e > 0 && (q.value = !0);
|
|
73
|
-
});
|
|
74
|
-
let J = p(() => G.value?.daily?.[K.value]), Y = p(() => {
|
|
75
|
-
if (!G.value?.hourly?.length || !J.value) return G.value?.hourly ?? [];
|
|
76
|
-
let e = J.value.date;
|
|
77
|
-
return G.value.hourly.filter((t) => t.time.startsWith(e));
|
|
78
|
-
});
|
|
79
|
-
function X(e) {
|
|
80
|
-
let t = new Date(e);
|
|
81
|
-
if (!Number.isNaN(t.getTime())) return t;
|
|
82
|
-
if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}/.test(e)) {
|
|
83
|
-
let t = new Date(e.replace(" ", "T"));
|
|
84
|
-
if (!Number.isNaN(t.getTime())) return t;
|
|
85
|
-
}
|
|
86
|
-
let n = e.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?$/);
|
|
87
|
-
if (n) {
|
|
88
|
-
let e = /* @__PURE__ */ new Date();
|
|
89
|
-
return e.setHours(Number(n[1]), Number(n[2]), Number(n[3] ?? 0), 0), e;
|
|
90
|
-
}
|
|
91
|
-
if (/^\d{4}-\d{2}-\d{2}$/.test(e)) {
|
|
92
|
-
let t = /* @__PURE__ */ new Date(e + "T12:00:00");
|
|
93
|
-
if (!Number.isNaN(t.getTime())) return t;
|
|
94
|
-
}
|
|
95
|
-
return /* @__PURE__ */ new Date(NaN);
|
|
96
|
-
}
|
|
97
|
-
let _e = {
|
|
98
|
-
clear: "wi:day-sunny",
|
|
99
|
-
mainlyClear: "wi:day-sunny-overcast",
|
|
100
|
-
partlyCloudy: "wi:day-cloudy",
|
|
101
|
-
overcast: "wi:cloudy",
|
|
102
|
-
fog: "wi:fog",
|
|
103
|
-
drizzle: "wi:sprinkle",
|
|
104
|
-
freezingDrizzle: "wi:sleet",
|
|
105
|
-
rain: "wi:rain",
|
|
106
|
-
freezingRain: "wi:rain-mix",
|
|
107
|
-
snow: "wi:snow",
|
|
108
|
-
rainShowers: "wi:showers",
|
|
109
|
-
snowShowers: "wi:snow-wind",
|
|
110
|
-
thunderstorm: "wi:thunderstorm",
|
|
111
|
-
thunderstormHail: "wi:storm-showers",
|
|
112
|
-
unknown: "wi:na"
|
|
113
|
-
};
|
|
114
|
-
function Z(e) {
|
|
115
|
-
return _e[re(e)];
|
|
116
|
-
}
|
|
117
|
-
function Q(e, t, n) {
|
|
118
|
-
let r = X(e + "T12:00:00");
|
|
119
|
-
if (!Number.isNaN(r.getTime())) {
|
|
120
|
-
let e = /* @__PURE__ */ new Date();
|
|
121
|
-
return e.setHours(12, 0, 0, 0), Math.round((r.getTime() - e.getTime()) / 864e5) === 0 ? a("weather.today") : f(r, "weekday-day");
|
|
122
|
-
}
|
|
123
|
-
if (t === 0 && n <= 7) return a("weather.today");
|
|
124
|
-
let i = /* @__PURE__ */ new Date();
|
|
125
|
-
return i.setDate(i.getDate() + t), f(i, "weekday-day");
|
|
126
|
-
}
|
|
127
|
-
let ve = p(() => {
|
|
128
|
-
if (J.value) {
|
|
129
|
-
let e = X(J.value.date + "T12:00:00");
|
|
130
|
-
if (!Number.isNaN(e.getTime())) return Q(J.value.date, K.value, G.value?.daily?.length ?? 1);
|
|
131
|
-
}
|
|
132
|
-
return Q((/* @__PURE__ */ new Date()).toISOString().slice(0, 10), 0, 1);
|
|
133
|
-
}), ye = {
|
|
134
|
-
good: {
|
|
135
|
-
labelKey: "weather.aqiGood",
|
|
136
|
-
class: "text-green-600 bg-green-50"
|
|
137
|
-
},
|
|
138
|
-
fair: {
|
|
139
|
-
labelKey: "weather.aqiFair",
|
|
140
|
-
class: "text-lime-600 bg-lime-50"
|
|
141
|
-
},
|
|
142
|
-
moderate: {
|
|
143
|
-
labelKey: "weather.aqiModerate",
|
|
144
|
-
class: "text-yellow-600 bg-yellow-50"
|
|
145
|
-
},
|
|
146
|
-
poor: {
|
|
147
|
-
labelKey: "weather.aqiPoor",
|
|
148
|
-
class: "text-orange-600 bg-orange-50"
|
|
149
|
-
},
|
|
150
|
-
veryPoor: {
|
|
151
|
-
labelKey: "weather.aqiVeryPoor",
|
|
152
|
-
class: "text-red-600 bg-red-50"
|
|
153
|
-
},
|
|
154
|
-
hazardous: {
|
|
155
|
-
labelKey: "weather.aqiHazardous",
|
|
156
|
-
class: "text-purple-700 bg-purple-50"
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
function $(e) {
|
|
160
|
-
let { labelKey: t, class: n } = ye[S(e).key];
|
|
161
|
-
return {
|
|
162
|
-
label: a(t),
|
|
163
|
-
class: n
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
function be(e) {
|
|
167
|
-
return [
|
|
168
|
-
"N",
|
|
169
|
-
"NE",
|
|
170
|
-
"E",
|
|
171
|
-
"SE",
|
|
172
|
-
"S",
|
|
173
|
-
"SO",
|
|
174
|
-
"O",
|
|
175
|
-
"NO"
|
|
176
|
-
][Math.round(e / 45) % 8];
|
|
177
|
-
}
|
|
178
|
-
return (t, n) => {
|
|
179
|
-
let s = ne, p = x;
|
|
180
|
-
return G.value ? (i(), _("div", ie, [
|
|
181
|
-
d("div", ae, [
|
|
182
|
-
y(s, {
|
|
183
|
-
name: "ri:temp-cold-line",
|
|
184
|
-
class: "text-16"
|
|
185
|
-
}),
|
|
186
|
-
d("strong", oe, r(G.value.locationName), 1),
|
|
187
|
-
d("span", se, r(ve.value), 1)
|
|
188
|
-
]),
|
|
189
|
-
G.value.current && K.value === 0 ? (i(), _("div", ce, [d("div", le, [y(s, {
|
|
190
|
-
name: Z(G.value.current.weatherCode),
|
|
191
|
-
class: "text-48 text-word-2 shrink-0"
|
|
192
|
-
}, null, 8, ["name"]), d("span", C, r(o(h)(G.value.current.temperature, "integer")) + "° ", 1)]), d("div", w, [
|
|
193
|
-
d("div", T, r(o(a)("weather.feelsLike")) + " " + r(o(h)(G.value.current.feelsLike, "integer")) + "° ", 1),
|
|
194
|
-
d("div", E, [y(s, {
|
|
195
|
-
name: "ri:water-percent-line",
|
|
196
|
-
class: "text-14"
|
|
197
|
-
}), l(" " + r(G.value.current.humidity) + "% ", 1)]),
|
|
198
|
-
d("div", D, [y(s, {
|
|
199
|
-
name: "ri:windy-line",
|
|
200
|
-
class: "text-14"
|
|
201
|
-
}), l(" " + r(o(h)(G.value.current.windSpeed, "integer")) + " km/h " + r(be(G.value.current.windDirection)), 1)]),
|
|
202
|
-
G.value.current.uvIndex > 0 ? (i(), _("div", O, [y(s, {
|
|
203
|
-
name: "ri:sun-line",
|
|
204
|
-
class: "text-14"
|
|
205
|
-
}), l(" UV " + r(o(h)(G.value.current.uvIndex, "integer")), 1)])) : c("", !0)
|
|
206
|
-
])])) : J.value ? (i(), _("div", k, [d("div", A, [y(s, {
|
|
207
|
-
name: Z(J.value.weatherCode),
|
|
208
|
-
class: "text-48 text-word-2 shrink-0"
|
|
209
|
-
}, null, 8, ["name"]), d("div", j, [d("span", M, r(o(h)(J.value.maxTemp, "integer")) + "° ", 1), d("span", N, r(o(h)(J.value.minTemp, "integer")) + "° ", 1)])]), d("div", P, [
|
|
210
|
-
J.value.precipitationProbabilityMax > 0 ? (i(), _("div", F, [y(s, {
|
|
211
|
-
name: "ri:drop-line",
|
|
212
|
-
class: "text-14"
|
|
213
|
-
}), l(" " + r(J.value.precipitationProbabilityMax) + "% (" + r(J.value.precipitationSum) + " mm) ", 1)])) : c("", !0),
|
|
214
|
-
J.value.uvIndexMax > 0 ? (i(), _("div", I, [y(s, {
|
|
215
|
-
name: "ri:sun-line",
|
|
216
|
-
class: "text-14"
|
|
217
|
-
}), l(" UV " + r(o(h)(J.value.uvIndexMax, "integer")), 1)])) : c("", !0),
|
|
218
|
-
d("div", L, [y(s, {
|
|
219
|
-
name: "ri:sun-foggy-line",
|
|
220
|
-
class: "text-14"
|
|
221
|
-
}), l(" " + r(o(f)(X(J.value.sunrise), "time")) + " – " + r(o(f)(X(J.value.sunset), "time")), 1)])
|
|
222
|
-
])])) : c("", !0),
|
|
223
|
-
y(g, null, {
|
|
224
|
-
default: e(() => [Y.value.length > 0 ? (i(), _("div", R, [d("div", z, [(i(!0), _(u, null, b(Y.value, (e) => (i(), _("div", {
|
|
225
|
-
key: e.time,
|
|
226
|
-
class: "flex flex-col items-center gap-4 min-w-48 px-4 py-6 rounded-xl text-11 text-word-3 shrink-0"
|
|
227
|
-
}, [
|
|
228
|
-
d("span", B, r(Number.isNaN(X(e.time).getTime()) ? e.time : o(f)(X(e.time), "time")), 1),
|
|
229
|
-
y(s, {
|
|
230
|
-
name: Z(e.weatherCode),
|
|
231
|
-
class: "text-20 text-word-2"
|
|
232
|
-
}, null, 8, ["name"]),
|
|
233
|
-
d("span", V, r(o(h)(e.temperature, "integer")) + "° ", 1),
|
|
234
|
-
e.precipitationProbability > 0 ? (i(), _("span", ue, r(e.precipitationProbability) + "% ", 1)) : c("", !0)
|
|
235
|
-
]))), 128))])])) : c("", !0)]),
|
|
236
|
-
_: 1
|
|
237
|
-
}),
|
|
238
|
-
y(g, null, {
|
|
239
|
-
default: e(() => [G.value.daily?.length > 1 ? (i(), ee(p, {
|
|
240
|
-
key: 0,
|
|
241
|
-
modelValue: q.value,
|
|
242
|
-
"onUpdate:modelValue": n[0] ||= (e) => q.value = e,
|
|
243
|
-
modifiers: "details no-padding",
|
|
244
|
-
title: o(a)("weather.dailyForecast"),
|
|
245
|
-
class: "border-t border-surface-3"
|
|
246
|
-
}, {
|
|
247
|
-
default: e(() => [(i(!0), _(u, null, b(G.value.daily, (e, t) => (i(), _("button", {
|
|
248
|
-
key: e.date,
|
|
249
|
-
type: "button",
|
|
250
|
-
class: v(["flex items-center gap-sm px-sm py-8 text-12 w-full text-left transition-colors cursor-pointer border-t border-surface-3", t === K.value ? "bg-surface-2" : "hover:bg-surface-1"]),
|
|
251
|
-
onClick: (e) => K.value = t
|
|
252
|
-
}, [
|
|
253
|
-
d("span", U, r(Q(e.date, t, G.value.daily.length)), 1),
|
|
254
|
-
y(s, {
|
|
255
|
-
name: Z(e.weatherCode),
|
|
256
|
-
class: "text-18 text-word-2 shrink-0"
|
|
257
|
-
}, null, 8, ["name"]),
|
|
258
|
-
e.precipitationProbabilityMax > 0 ? (i(), _("span", de, r(e.precipitationProbabilityMax) + "% ", 1)) : (i(), _("span", fe)),
|
|
259
|
-
d("div", pe, [d("span", me, r(o(h)(e.maxTemp, "integer")) + "° ", 1), d("span", null, r(o(h)(e.minTemp, "integer")) + "°", 1)])
|
|
260
|
-
], 10, H))), 128))]),
|
|
261
|
-
_: 1
|
|
262
|
-
}, 8, ["modelValue", "title"])) : c("", !0)]),
|
|
263
|
-
_: 1
|
|
264
|
-
}),
|
|
265
|
-
G.value.airQuality ? (i(), _("div", he, [
|
|
266
|
-
y(s, {
|
|
267
|
-
name: "ri:windy-line",
|
|
268
|
-
class: "text-14 text-word-3"
|
|
269
|
-
}),
|
|
270
|
-
d("span", ge, r(o(a)("weather.airQuality")), 1),
|
|
271
|
-
d("span", { class: v(["ml-auto px-8 py-2 text-11 font-semibold", $(G.value.airQuality.aqi).class]) }, r($(G.value.airQuality.aqi).label) + " (" + r(G.value.airQuality.aqi) + ") ", 3)
|
|
272
|
-
])) : c("", !0)
|
|
273
|
-
])) : c("", !0);
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
//#endregion
|
|
278
|
-
export { W as default };
|
|
279
|
-
|
|
280
|
-
//# sourceMappingURL=PkToolShowWeather-BFOlS1AS.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PkToolShowWeather-BFOlS1AS.js","names":["__unplugin_components_0","__unplugin_components_1","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_toDisplayString","_unref","_createTextVNode","_createCommentVNode","_Transition","_withCtx","_Fragment","_renderList","_createBlock","_normalizeClass","_sfc_main","withCtx","defineComponent","init_shared_esm_bundler","toDisplayString","openBlock","init_runtime_core_esm_bundler","unref","watch","createCommentVNode","createTextVNode","Fragment","createBlock","createBaseVNode","init_reactivity_esm_bundler","computed","ref","init_runtime_dom_esm_bundler","Transition","createElementBlock","normalizeClass","createVNode","renderList","useI18n","Ar","Ae","wmoCategory","code","aqiBand","aqi","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","_hoisted_28","_hoisted_29","_hoisted_30","_hoisted_31","_hoisted_32","_hoisted_33","_hoisted_34","PkToolShowWeather_default","props","__props","$t","$d","$n","toolPart","data","selectedDayIndex","dailyOpen","index","selectedDay","filteredHourly","dayDate","h","safeDate","direct","value","withT","timeOnly","today","noon","WMO_ICON","wmoToIcon","formatDay","parsed","isoDate","total","fallback","headerDate","AQI_PRESENTATION","aqiInfo","cssClass","labelKey","windDirectionLabel","deg","_component_VvIcon","_component_VvAccordion","hour","_cache","$event","day"],"sources":["../../../../packages/utils/src/weather.ts","../../../../packages/components/src/chat/PkToolShowWeather.vue","../../../../packages/components/src/chat/PkToolShowWeather.vue"],"sourcesContent":["/**\n * Framework-agnostic weather presentation primitives. They classify raw\n * open-meteo values into canonical categories; each consumer (web widget,\n * platform card renderer) maps a category to its own representation (an\n * Iconify name, an emoji, a CSS class, an i18n key). Keeping the classification\n * here means the WMO code bands and AQI thresholds live in exactly one place.\n */\n\n/** Canonical WMO weather-code category (resolution matches the web widget). */\nexport type WmoCategory =\n | 'clear'\n | 'mainlyClear'\n | 'partlyCloudy'\n | 'overcast'\n | 'fog'\n | 'drizzle'\n | 'freezingDrizzle'\n | 'rain'\n | 'freezingRain'\n | 'snow'\n | 'rainShowers'\n | 'snowShowers'\n | 'thunderstorm'\n | 'thunderstormHail'\n | 'unknown'\n\n/** Map a WMO weather interpretation code to a canonical category. */\nexport function wmoCategory(code: number | undefined): WmoCategory {\n if (code === undefined) {\n return 'unknown'\n }\n if (code === 0) {\n return 'clear'\n }\n if (code === 1) {\n return 'mainlyClear'\n }\n if (code === 2) {\n return 'partlyCloudy'\n }\n if (code === 3) {\n return 'overcast'\n }\n if (code === 45 || code === 48) {\n return 'fog'\n }\n if (code >= 51 && code <= 55) {\n return 'drizzle'\n }\n if (code === 56 || code === 57) {\n return 'freezingDrizzle'\n }\n if (code >= 61 && code <= 65) {\n return 'rain'\n }\n if (code === 66 || code === 67) {\n return 'freezingRain'\n }\n if (code >= 71 && code <= 77) {\n return 'snow'\n }\n if (code >= 80 && code <= 82) {\n return 'rainShowers'\n }\n if (code === 85 || code === 86) {\n return 'snowShowers'\n }\n if (code === 95) {\n return 'thunderstorm'\n }\n if (code === 96 || code === 99) {\n return 'thunderstormHail'\n }\n return 'unknown'\n}\n\n/** Emoji per category (used by the language-neutral platform card renderer). */\nconst WMO_EMOJI: Record<WmoCategory, string> = {\n clear: '☀️',\n mainlyClear: '⛅',\n partlyCloudy: '⛅',\n overcast: '⛅',\n fog: '🌫️',\n drizzle: '🌦️',\n freezingDrizzle: '🌦️',\n rain: '🌧️',\n freezingRain: '🌧️',\n snow: '❄️',\n rainShowers: '🌧️',\n snowShowers: '🌨️',\n thunderstorm: '⛈️',\n thunderstormHail: '⛈️',\n unknown: '🌡️',\n}\n\n/** WMO code → emoji (convenience for non-UI consumers). */\nexport function wmoEmoji(code: number | undefined): string {\n return WMO_EMOJI[wmoCategory(code)]\n}\n\n/** Canonical European-AQI band, ordered best → worst. */\nexport type AqiBandKey =\n 'good' | 'fair' | 'moderate' | 'poor' | 'veryPoor' | 'hazardous'\n\n/** Classify a European AQI value into a band (index + canonical key). */\nexport function aqiBand(aqi: number): { index: number; key: AqiBandKey } {\n if (aqi <= 20) {\n return { index: 0, key: 'good' }\n }\n if (aqi <= 40) {\n return { index: 1, key: 'fair' }\n }\n if (aqi <= 60) {\n return { index: 2, key: 'moderate' }\n }\n if (aqi <= 80) {\n return { index: 3, key: 'poor' }\n }\n if (aqi <= 100) {\n return { index: 4, key: 'veryPoor' }\n }\n return { index: 5, key: 'hazardous' }\n}\n\n/** Colored dot per AQI band index (used by the platform card renderer). */\nconst AQI_DOTS = ['🟢', '🟡', '🟠', '🔴', '🟣', '⚫']\n\n/** AQI value → colored dot (convenience for non-UI consumers). */\nexport function aqiDot(aqi: number): string {\n return AQI_DOTS[aqiBand(aqi).index]\n}\n","<script setup lang=\"ts\">\n import { computed, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import type { WeatherData } from 'models'\n import {\n aqiBand,\n wmoCategory,\n type AqiBandKey,\n type WmoCategory,\n } from 'utils'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { t: $t, d: $d, n: $n } = useI18n({ useScope: 'global' })\n\n const toolPart = computed(() => {\n return props.part as { input?: WeatherData; output?: WeatherData }\n })\n\n const data = computed(\n () => toolPart.value.output ?? toolPart.value.input ?? null,\n )\n\n const selectedDayIndex = ref(0)\n\n const dailyOpen = ref(!data.value?.current)\n\n watch(selectedDayIndex, (index) => {\n if (index > 0) {\n dailyOpen.value = true\n }\n })\n\n const selectedDay = computed(\n () => data.value?.daily?.[selectedDayIndex.value],\n )\n\n /** Hourly entries filtered to the selected day's date */\n const filteredHourly = computed(() => {\n if (!data.value?.hourly?.length || !selectedDay.value) {\n return data.value?.hourly ?? []\n }\n const dayDate = selectedDay.value.date\n return data.value.hourly.filter((h) => h.time.startsWith(dayDate))\n })\n\n /**\n * Parses a date/time string in various formats the LLM might produce.\n * Tries ISO, space-separated, time-only, and common human-readable formats.\n */\n function safeDate(value: string): Date {\n // 1. Try native parsing (works for ISO 8601 and many standard formats)\n const direct = new Date(value)\n if (!Number.isNaN(direct.getTime())) {\n return direct\n }\n\n // 2. Replace space separator → T (\"2024-04-15 14:00\" → \"2024-04-15T14:00\")\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}/.test(value)) {\n const withT = new Date(value.replace(' ', 'T'))\n if (!Number.isNaN(withT.getTime())) {\n return withT\n }\n }\n\n // 3. Time-only (\"14:00\" or \"14:00:00\") → today at that hour\n const timeOnly = value.match(/^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/)\n if (timeOnly) {\n const today = new Date()\n today.setHours(\n Number(timeOnly[1]),\n Number(timeOnly[2]),\n Number(timeOnly[3] ?? 0),\n 0,\n )\n return today\n }\n\n // 4. Date-only (\"2024-04-15\") → noon to avoid timezone edge\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n const noon = new Date(value + 'T12:00:00')\n if (!Number.isNaN(noon.getTime())) {\n return noon\n }\n }\n\n // Last resort: return an obviously invalid sentinel so callers can handle it\n return new Date(NaN)\n }\n\n /** Iconify `wi:` icon per canonical WMO category. */\n const WMO_ICON: Record<WmoCategory, string> = {\n clear: 'wi:day-sunny',\n mainlyClear: 'wi:day-sunny-overcast',\n partlyCloudy: 'wi:day-cloudy',\n overcast: 'wi:cloudy',\n fog: 'wi:fog',\n drizzle: 'wi:sprinkle',\n freezingDrizzle: 'wi:sleet',\n rain: 'wi:rain',\n freezingRain: 'wi:rain-mix',\n snow: 'wi:snow',\n rainShowers: 'wi:showers',\n snowShowers: 'wi:snow-wind',\n thunderstorm: 'wi:thunderstorm',\n thunderstormHail: 'wi:storm-showers',\n unknown: 'wi:na',\n }\n\n /** Maps WMO weather code to an Iconify wi: icon name */\n function wmoToIcon(code: number): string {\n return WMO_ICON[wmoCategory(code)]\n }\n\n /**\n * Returns a formatted day label for daily forecast.\n * When daily has 1-2 entries, use the date string directly.\n * Otherwise, use index-based computation from today.\n */\n function formatDay(isoDate: string, index: number, total: number): string {\n const parsed = safeDate(isoDate + 'T12:00:00')\n if (!Number.isNaN(parsed.getTime())) {\n const today = new Date()\n today.setHours(12, 0, 0, 0)\n const diffDays = Math.round(\n (parsed.getTime() - today.getTime()) / 86_400_000,\n )\n if (diffDays === 0) {\n return $t('weather.today')\n }\n return $d(parsed, 'weekday-day')\n }\n if (index === 0 && total <= 7) {\n return $t('weather.today')\n }\n const fallback = new Date()\n fallback.setDate(fallback.getDate() + index)\n return $d(fallback, 'weekday-day')\n }\n\n const headerDate = computed(() => {\n if (selectedDay.value) {\n const parsed = safeDate(selectedDay.value.date + 'T12:00:00')\n if (!Number.isNaN(parsed.getTime())) {\n return formatDay(\n selectedDay.value.date,\n selectedDayIndex.value,\n data.value?.daily?.length ?? 1,\n )\n }\n }\n return formatDay(new Date().toISOString().slice(0, 10), 0, 1)\n })\n\n /** i18n label key + CSS class per canonical AQI band. */\n const AQI_PRESENTATION: Record<\n AqiBandKey,\n { labelKey: string; class: string }\n > = {\n good: {\n labelKey: 'weather.aqiGood',\n class: 'text-green-600 bg-green-50',\n },\n fair: {\n labelKey: 'weather.aqiFair',\n class: 'text-lime-600 bg-lime-50',\n },\n moderate: {\n labelKey: 'weather.aqiModerate',\n class: 'text-yellow-600 bg-yellow-50',\n },\n poor: {\n labelKey: 'weather.aqiPoor',\n class: 'text-orange-600 bg-orange-50',\n },\n veryPoor: {\n labelKey: 'weather.aqiVeryPoor',\n class: 'text-red-600 bg-red-50',\n },\n hazardous: {\n labelKey: 'weather.aqiHazardous',\n class: 'text-purple-700 bg-purple-50',\n },\n }\n\n /** Returns AQI label and CSS class for European AQI */\n function aqiInfo(aqi: number): { label: string; class: string } {\n const { labelKey, class: cssClass } = AQI_PRESENTATION[aqiBand(aqi).key]\n return { label: $t(labelKey), class: cssClass }\n }\n\n /** Formats wind direction degrees to compass label */\n function windDirectionLabel(deg: number): string {\n const dirs = ['N', 'NE', 'E', 'SE', 'S', 'SO', 'O', 'NO']\n return dirs[Math.round(deg / 45) % 8]\n }\n</script>\n\n<template>\n <div\n v-if=\"data\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <!-- Header -->\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:temp-cold-line\" class=\"text-16\" />\n <strong class=\"font-bold truncate\">\n {{ data.locationName }}\n </strong>\n <span class=\"ml-auto shrink-0 capitalize\">\n {{ headerDate }}\n </span>\n </div>\n\n <!-- Current conditions (only when real-time current is available AND first day is selected) -->\n <div\n v-if=\"data.current && selectedDayIndex === 0\"\n class=\"px-sm py-md flex items-center gap-md flex-wrap\">\n <div class=\"flex items-center gap-8\">\n <VvIcon\n :name=\"wmoToIcon(data.current.weatherCode)\"\n class=\"text-48 text-word-2 shrink-0\" />\n <span class=\"text-48 font-light leading-none text-word-1\">\n {{ $n(data.current.temperature, 'integer') }}°\n </span>\n </div>\n <div class=\"flex flex-col gap-4 text-12 text-word-3\">\n <div class=\"flex items-center gap-6\">\n {{ $t('weather.feelsLike') }}\n {{ $n(data.current.feelsLike, 'integer') }}°\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:water-percent-line\" class=\"text-14\" />\n {{ data.current.humidity }}%\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:windy-line\" class=\"text-14\" />\n {{ $n(data.current.windSpeed, 'integer') }} km/h\n {{ windDirectionLabel(data.current.windDirection) }}\n </div>\n <div\n v-if=\"data.current.uvIndex > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-line\" class=\"text-14\" />\n UV {{ $n(data.current.uvIndex, 'integer') }}\n </div>\n </div>\n </div>\n\n <!-- Day summary (when no real-time current, or a non-today day is selected) -->\n <div\n v-else-if=\"selectedDay\"\n class=\"px-sm py-md flex items-center gap-md flex-wrap\">\n <div class=\"flex items-center gap-8\">\n <VvIcon\n :name=\"wmoToIcon(selectedDay.weatherCode)\"\n class=\"text-48 text-word-2 shrink-0\" />\n <div class=\"flex flex-col leading-none\">\n <span class=\"text-48 font-light text-word-1\">\n {{ $n(selectedDay.maxTemp, 'integer') }}°\n </span>\n <span class=\"text-20 text-word-3\">\n {{ $n(selectedDay.minTemp, 'integer') }}°\n </span>\n </div>\n </div>\n <div class=\"flex flex-col gap-4 text-12 text-word-3\">\n <div\n v-if=\"selectedDay.precipitationProbabilityMax > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:drop-line\" class=\"text-14\" />\n {{ selectedDay.precipitationProbabilityMax }}% ({{\n selectedDay.precipitationSum\n }}\n mm)\n </div>\n <div\n v-if=\"selectedDay.uvIndexMax > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-line\" class=\"text-14\" />\n UV {{ $n(selectedDay.uvIndexMax, 'integer') }}\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-foggy-line\" class=\"text-14\" />\n {{ $d(safeDate(selectedDay.sunrise), 'time') }}\n –\n {{ $d(safeDate(selectedDay.sunset), 'time') }}\n </div>\n </div>\n </div>\n\n <!-- Hourly forecast (filtered to selected day) -->\n <transition>\n <div\n v-if=\"filteredHourly.length > 0\"\n class=\"border-t border-surface-3\">\n <div\n class=\"flex overflow-x-auto gap-2 px-sm py-8 light-scrollbar\">\n <div\n v-for=\"hour in filteredHourly\"\n :key=\"hour.time\"\n class=\"flex flex-col items-center gap-4 min-w-48 px-4 py-6 rounded-xl text-11 text-word-3 shrink-0\">\n <span class=\"text-word-3\">{{\n Number.isNaN(safeDate(hour.time).getTime())\n ? hour.time\n : $d(safeDate(hour.time), 'time')\n }}</span>\n <VvIcon\n :name=\"wmoToIcon(hour.weatherCode)\"\n class=\"text-20 text-word-2\" />\n <span class=\"font-medium text-word-1\">\n {{ $n(hour.temperature, 'integer') }}°\n </span>\n <span\n v-if=\"hour.precipitationProbability > 0\"\n class=\"text-word-5 text-smaller\">\n {{ hour.precipitationProbability }}%\n </span>\n </div>\n </div>\n </div>\n </transition>\n\n <!-- Daily forecast (clickable, inside accordion) -->\n <transition>\n <VvAccordion\n v-if=\"data.daily?.length > 1\"\n v-model=\"dailyOpen\"\n modifiers=\"details no-padding\"\n :title=\"$t('weather.dailyForecast')\"\n class=\"border-t border-surface-3\">\n <button\n v-for=\"(day, index) in data.daily\"\n :key=\"day.date\"\n type=\"button\"\n class=\"flex items-center gap-sm px-sm py-8 text-12 w-full text-left transition-colors cursor-pointer border-t border-surface-3\"\n :class=\"\n index === selectedDayIndex\n ? 'bg-surface-2'\n : 'hover:bg-surface-1'\n \"\n @click=\"selectedDayIndex = index\">\n <span class=\"w-48 shrink-0 text-word-3 capitalize\">\n {{ formatDay(day.date, index, data.daily.length) }}\n </span>\n <VvIcon\n :name=\"wmoToIcon(day.weatherCode)\"\n class=\"text-18 text-word-2 shrink-0\" />\n <span\n v-if=\"day.precipitationProbabilityMax > 0\"\n class=\"text-word-5 text-11 w-32 shrink-0\">\n {{ day.precipitationProbabilityMax }}%\n </span>\n <span v-else class=\"w-32 shrink-0\" />\n <div class=\"ml-auto flex items-center gap-8 text-word-3\">\n <span class=\"text-word-1 font-semibold\">\n {{ $n(day.maxTemp, 'integer') }}°\n </span>\n <span>{{ $n(day.minTemp, 'integer') }}°</span>\n </div>\n </button>\n </VvAccordion>\n </transition>\n\n <!-- Air quality -->\n <div\n v-if=\"data.airQuality\"\n class=\"border-t border-surface-3 px-sm py-8 flex items-center gap-8 text-12\">\n <VvIcon name=\"ri:windy-line\" class=\"text-14 text-word-3\" />\n <span class=\"text-word-3\">{{ $t('weather.airQuality') }}</span>\n <span\n :class=\"[\n 'ml-auto px-8 py-2 text-11 font-semibold',\n aqiInfo(data.airQuality.aqi).class,\n ]\">\n {{ aqiInfo(data.airQuality.aqi).label }}\n ({{ data.airQuality.aqi }})\n </span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import type { WeatherData } from 'models'\n import {\n aqiBand,\n wmoCategory,\n type AqiBandKey,\n type WmoCategory,\n } from 'utils'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const { t: $t, d: $d, n: $n } = useI18n({ useScope: 'global' })\n\n const toolPart = computed(() => {\n return props.part as { input?: WeatherData; output?: WeatherData }\n })\n\n const data = computed(\n () => toolPart.value.output ?? toolPart.value.input ?? null,\n )\n\n const selectedDayIndex = ref(0)\n\n const dailyOpen = ref(!data.value?.current)\n\n watch(selectedDayIndex, (index) => {\n if (index > 0) {\n dailyOpen.value = true\n }\n })\n\n const selectedDay = computed(\n () => data.value?.daily?.[selectedDayIndex.value],\n )\n\n /** Hourly entries filtered to the selected day's date */\n const filteredHourly = computed(() => {\n if (!data.value?.hourly?.length || !selectedDay.value) {\n return data.value?.hourly ?? []\n }\n const dayDate = selectedDay.value.date\n return data.value.hourly.filter((h) => h.time.startsWith(dayDate))\n })\n\n /**\n * Parses a date/time string in various formats the LLM might produce.\n * Tries ISO, space-separated, time-only, and common human-readable formats.\n */\n function safeDate(value: string): Date {\n // 1. Try native parsing (works for ISO 8601 and many standard formats)\n const direct = new Date(value)\n if (!Number.isNaN(direct.getTime())) {\n return direct\n }\n\n // 2. Replace space separator → T (\"2024-04-15 14:00\" → \"2024-04-15T14:00\")\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}/.test(value)) {\n const withT = new Date(value.replace(' ', 'T'))\n if (!Number.isNaN(withT.getTime())) {\n return withT\n }\n }\n\n // 3. Time-only (\"14:00\" or \"14:00:00\") → today at that hour\n const timeOnly = value.match(/^(\\d{1,2}):(\\d{2})(?::(\\d{2}))?$/)\n if (timeOnly) {\n const today = new Date()\n today.setHours(\n Number(timeOnly[1]),\n Number(timeOnly[2]),\n Number(timeOnly[3] ?? 0),\n 0,\n )\n return today\n }\n\n // 4. Date-only (\"2024-04-15\") → noon to avoid timezone edge\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n const noon = new Date(value + 'T12:00:00')\n if (!Number.isNaN(noon.getTime())) {\n return noon\n }\n }\n\n // Last resort: return an obviously invalid sentinel so callers can handle it\n return new Date(NaN)\n }\n\n /** Iconify `wi:` icon per canonical WMO category. */\n const WMO_ICON: Record<WmoCategory, string> = {\n clear: 'wi:day-sunny',\n mainlyClear: 'wi:day-sunny-overcast',\n partlyCloudy: 'wi:day-cloudy',\n overcast: 'wi:cloudy',\n fog: 'wi:fog',\n drizzle: 'wi:sprinkle',\n freezingDrizzle: 'wi:sleet',\n rain: 'wi:rain',\n freezingRain: 'wi:rain-mix',\n snow: 'wi:snow',\n rainShowers: 'wi:showers',\n snowShowers: 'wi:snow-wind',\n thunderstorm: 'wi:thunderstorm',\n thunderstormHail: 'wi:storm-showers',\n unknown: 'wi:na',\n }\n\n /** Maps WMO weather code to an Iconify wi: icon name */\n function wmoToIcon(code: number): string {\n return WMO_ICON[wmoCategory(code)]\n }\n\n /**\n * Returns a formatted day label for daily forecast.\n * When daily has 1-2 entries, use the date string directly.\n * Otherwise, use index-based computation from today.\n */\n function formatDay(isoDate: string, index: number, total: number): string {\n const parsed = safeDate(isoDate + 'T12:00:00')\n if (!Number.isNaN(parsed.getTime())) {\n const today = new Date()\n today.setHours(12, 0, 0, 0)\n const diffDays = Math.round(\n (parsed.getTime() - today.getTime()) / 86_400_000,\n )\n if (diffDays === 0) {\n return $t('weather.today')\n }\n return $d(parsed, 'weekday-day')\n }\n if (index === 0 && total <= 7) {\n return $t('weather.today')\n }\n const fallback = new Date()\n fallback.setDate(fallback.getDate() + index)\n return $d(fallback, 'weekday-day')\n }\n\n const headerDate = computed(() => {\n if (selectedDay.value) {\n const parsed = safeDate(selectedDay.value.date + 'T12:00:00')\n if (!Number.isNaN(parsed.getTime())) {\n return formatDay(\n selectedDay.value.date,\n selectedDayIndex.value,\n data.value?.daily?.length ?? 1,\n )\n }\n }\n return formatDay(new Date().toISOString().slice(0, 10), 0, 1)\n })\n\n /** i18n label key + CSS class per canonical AQI band. */\n const AQI_PRESENTATION: Record<\n AqiBandKey,\n { labelKey: string; class: string }\n > = {\n good: {\n labelKey: 'weather.aqiGood',\n class: 'text-green-600 bg-green-50',\n },\n fair: {\n labelKey: 'weather.aqiFair',\n class: 'text-lime-600 bg-lime-50',\n },\n moderate: {\n labelKey: 'weather.aqiModerate',\n class: 'text-yellow-600 bg-yellow-50',\n },\n poor: {\n labelKey: 'weather.aqiPoor',\n class: 'text-orange-600 bg-orange-50',\n },\n veryPoor: {\n labelKey: 'weather.aqiVeryPoor',\n class: 'text-red-600 bg-red-50',\n },\n hazardous: {\n labelKey: 'weather.aqiHazardous',\n class: 'text-purple-700 bg-purple-50',\n },\n }\n\n /** Returns AQI label and CSS class for European AQI */\n function aqiInfo(aqi: number): { label: string; class: string } {\n const { labelKey, class: cssClass } = AQI_PRESENTATION[aqiBand(aqi).key]\n return { label: $t(labelKey), class: cssClass }\n }\n\n /** Formats wind direction degrees to compass label */\n function windDirectionLabel(deg: number): string {\n const dirs = ['N', 'NE', 'E', 'SE', 'S', 'SO', 'O', 'NO']\n return dirs[Math.round(deg / 45) % 8]\n }\n</script>\n\n<template>\n <div\n v-if=\"data\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <!-- Header -->\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:temp-cold-line\" class=\"text-16\" />\n <strong class=\"font-bold truncate\">\n {{ data.locationName }}\n </strong>\n <span class=\"ml-auto shrink-0 capitalize\">\n {{ headerDate }}\n </span>\n </div>\n\n <!-- Current conditions (only when real-time current is available AND first day is selected) -->\n <div\n v-if=\"data.current && selectedDayIndex === 0\"\n class=\"px-sm py-md flex items-center gap-md flex-wrap\">\n <div class=\"flex items-center gap-8\">\n <VvIcon\n :name=\"wmoToIcon(data.current.weatherCode)\"\n class=\"text-48 text-word-2 shrink-0\" />\n <span class=\"text-48 font-light leading-none text-word-1\">\n {{ $n(data.current.temperature, 'integer') }}°\n </span>\n </div>\n <div class=\"flex flex-col gap-4 text-12 text-word-3\">\n <div class=\"flex items-center gap-6\">\n {{ $t('weather.feelsLike') }}\n {{ $n(data.current.feelsLike, 'integer') }}°\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:water-percent-line\" class=\"text-14\" />\n {{ data.current.humidity }}%\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:windy-line\" class=\"text-14\" />\n {{ $n(data.current.windSpeed, 'integer') }} km/h\n {{ windDirectionLabel(data.current.windDirection) }}\n </div>\n <div\n v-if=\"data.current.uvIndex > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-line\" class=\"text-14\" />\n UV {{ $n(data.current.uvIndex, 'integer') }}\n </div>\n </div>\n </div>\n\n <!-- Day summary (when no real-time current, or a non-today day is selected) -->\n <div\n v-else-if=\"selectedDay\"\n class=\"px-sm py-md flex items-center gap-md flex-wrap\">\n <div class=\"flex items-center gap-8\">\n <VvIcon\n :name=\"wmoToIcon(selectedDay.weatherCode)\"\n class=\"text-48 text-word-2 shrink-0\" />\n <div class=\"flex flex-col leading-none\">\n <span class=\"text-48 font-light text-word-1\">\n {{ $n(selectedDay.maxTemp, 'integer') }}°\n </span>\n <span class=\"text-20 text-word-3\">\n {{ $n(selectedDay.minTemp, 'integer') }}°\n </span>\n </div>\n </div>\n <div class=\"flex flex-col gap-4 text-12 text-word-3\">\n <div\n v-if=\"selectedDay.precipitationProbabilityMax > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:drop-line\" class=\"text-14\" />\n {{ selectedDay.precipitationProbabilityMax }}% ({{\n selectedDay.precipitationSum\n }}\n mm)\n </div>\n <div\n v-if=\"selectedDay.uvIndexMax > 0\"\n class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-line\" class=\"text-14\" />\n UV {{ $n(selectedDay.uvIndexMax, 'integer') }}\n </div>\n <div class=\"flex items-center gap-6\">\n <VvIcon name=\"ri:sun-foggy-line\" class=\"text-14\" />\n {{ $d(safeDate(selectedDay.sunrise), 'time') }}\n –\n {{ $d(safeDate(selectedDay.sunset), 'time') }}\n </div>\n </div>\n </div>\n\n <!-- Hourly forecast (filtered to selected day) -->\n <transition>\n <div\n v-if=\"filteredHourly.length > 0\"\n class=\"border-t border-surface-3\">\n <div\n class=\"flex overflow-x-auto gap-2 px-sm py-8 light-scrollbar\">\n <div\n v-for=\"hour in filteredHourly\"\n :key=\"hour.time\"\n class=\"flex flex-col items-center gap-4 min-w-48 px-4 py-6 rounded-xl text-11 text-word-3 shrink-0\">\n <span class=\"text-word-3\">{{\n Number.isNaN(safeDate(hour.time).getTime())\n ? hour.time\n : $d(safeDate(hour.time), 'time')\n }}</span>\n <VvIcon\n :name=\"wmoToIcon(hour.weatherCode)\"\n class=\"text-20 text-word-2\" />\n <span class=\"font-medium text-word-1\">\n {{ $n(hour.temperature, 'integer') }}°\n </span>\n <span\n v-if=\"hour.precipitationProbability > 0\"\n class=\"text-word-5 text-smaller\">\n {{ hour.precipitationProbability }}%\n </span>\n </div>\n </div>\n </div>\n </transition>\n\n <!-- Daily forecast (clickable, inside accordion) -->\n <transition>\n <VvAccordion\n v-if=\"data.daily?.length > 1\"\n v-model=\"dailyOpen\"\n modifiers=\"details no-padding\"\n :title=\"$t('weather.dailyForecast')\"\n class=\"border-t border-surface-3\">\n <button\n v-for=\"(day, index) in data.daily\"\n :key=\"day.date\"\n type=\"button\"\n class=\"flex items-center gap-sm px-sm py-8 text-12 w-full text-left transition-colors cursor-pointer border-t border-surface-3\"\n :class=\"\n index === selectedDayIndex\n ? 'bg-surface-2'\n : 'hover:bg-surface-1'\n \"\n @click=\"selectedDayIndex = index\">\n <span class=\"w-48 shrink-0 text-word-3 capitalize\">\n {{ formatDay(day.date, index, data.daily.length) }}\n </span>\n <VvIcon\n :name=\"wmoToIcon(day.weatherCode)\"\n class=\"text-18 text-word-2 shrink-0\" />\n <span\n v-if=\"day.precipitationProbabilityMax > 0\"\n class=\"text-word-5 text-11 w-32 shrink-0\">\n {{ day.precipitationProbabilityMax }}%\n </span>\n <span v-else class=\"w-32 shrink-0\" />\n <div class=\"ml-auto flex items-center gap-8 text-word-3\">\n <span class=\"text-word-1 font-semibold\">\n {{ $n(day.maxTemp, 'integer') }}°\n </span>\n <span>{{ $n(day.minTemp, 'integer') }}°</span>\n </div>\n </button>\n </VvAccordion>\n </transition>\n\n <!-- Air quality -->\n <div\n v-if=\"data.airQuality\"\n class=\"border-t border-surface-3 px-sm py-8 flex items-center gap-8 text-12\">\n <VvIcon name=\"ri:windy-line\" class=\"text-14 text-word-3\" />\n <span class=\"text-word-3\">{{ $t('weather.airQuality') }}</span>\n <span\n :class=\"[\n 'ml-auto px-8 py-2 text-11 font-semibold',\n aqiInfo(data.airQuality.aqi).class,\n ]\">\n {{ aqiInfo(data.airQuality.aqi).label }}\n ({{ data.airQuality.aqi }})\n </span>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|