phoenix_duskmoon 10.0.11 → 10.1.0
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/CHANGELOG.md +39 -0
- package/README.md +12 -5
- package/assets/js/react_chat/chat.js +15 -16
- package/assets/js/react_chat/index.js +451 -40
- package/assets/js/react_chat/protocol.js +125 -0
- package/assets/js/react_chat/state.js +134 -5
- package/assets/js/react_form/index.js +1 -1
- package/package.json +24 -10
- package/priv/static/assets/css/app-79cf21e84f298332a3addc11d02afa5c.css +2 -0
- package/priv/static/assets/css/app-79cf21e84f298332a3addc11d02afa5c.css.gz +0 -0
- package/priv/static/assets/css/app.css +2 -2
- package/priv/static/assets/css/app.css.gz +0 -0
- package/priv/static/assets/js/{phoenix_duskmoon-c22eb67823f82d7b0d27eba5dbd7be12.js → phoenix_duskmoon-7a95bc687b7d72a85fec06221386e515.js} +1 -1
- package/priv/static/assets/js/{phoenix_duskmoon-c22eb67823f82d7b0d27eba5dbd7be12.js.gz → phoenix_duskmoon-7a95bc687b7d72a85fec06221386e515.js.gz} +0 -0
- package/priv/static/assets/js/phoenix_duskmoon.js +1 -1
- package/priv/static/assets/js/phoenix_duskmoon.js.gz +0 -0
- package/priv/static/cache_manifest.json +2 -2
- package/priv/static/assets/css/app-8305fbeb48a74c180d2d40e5e2a038c9.css +0 -2
- package/priv/static/assets/css/app-8305fbeb48a74c180d2d40e5e2a038c9.css.gz +0 -0
|
@@ -1,6 +1,63 @@
|
|
|
1
|
+
export function sameChatScope(left, right) {
|
|
2
|
+
return (
|
|
3
|
+
typeof left?.conversationId === "string" &&
|
|
4
|
+
left.conversationId.length > 0 &&
|
|
5
|
+
typeof left.contextKey === "string" &&
|
|
6
|
+
left.contextKey.length > 0 &&
|
|
7
|
+
left.conversationId === right?.conversationId &&
|
|
8
|
+
left.contextKey === right?.contextKey
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function revision(value) {
|
|
13
|
+
return Number.isSafeInteger(value) && value >= 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function requestSync(state) {
|
|
17
|
+
return state.needsSync ? state : { ...state, needsSync: true };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function validChatQueue(queue, scope) {
|
|
21
|
+
return (
|
|
22
|
+
queue?.protocolVersion === 1 &&
|
|
23
|
+
sameChatScope(queue.scope, scope) &&
|
|
24
|
+
revision(queue.revision) &&
|
|
25
|
+
Array.isArray(queue.items) &&
|
|
26
|
+
queue.items.every(
|
|
27
|
+
(item) => typeof item?.id === "string" && item.id.length > 0,
|
|
28
|
+
) &&
|
|
29
|
+
new Set(queue.items.map((item) => item.id)).size === queue.items.length
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function validProjection(composer, state) {
|
|
34
|
+
if (!composer || !revision(composer.revision)) return false;
|
|
35
|
+
const queue = composer.queue;
|
|
36
|
+
if (queue == null) return state.composer?.queue == null;
|
|
37
|
+
return (
|
|
38
|
+
validChatQueue(queue, state.scope) &&
|
|
39
|
+
queue.revision >= (state.composer?.queue?.revision ?? -1)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Each queue revision is immutable, independently of capabilities or runtime.
|
|
44
|
+
function nextProjection(composer, state) {
|
|
45
|
+
if (
|
|
46
|
+
state.composer?.queue &&
|
|
47
|
+
composer.queue?.revision === state.composer.queue.revision
|
|
48
|
+
) {
|
|
49
|
+
return { ...composer, queue: state.composer.queue };
|
|
50
|
+
}
|
|
51
|
+
return composer;
|
|
52
|
+
}
|
|
53
|
+
|
|
1
54
|
export function initialState(conversationId, messages = [], application = {}) {
|
|
2
55
|
return {
|
|
3
56
|
conversationId,
|
|
57
|
+
protocolVersion: application.protocolVersion ?? null,
|
|
58
|
+
scope: application.scope ?? null,
|
|
59
|
+
instanceGeneration: application.instanceGeneration ?? null,
|
|
60
|
+
composer: application.composer ?? null,
|
|
4
61
|
messages,
|
|
5
62
|
metadata: application.metadata || {},
|
|
6
63
|
runtime: application.runtime || {},
|
|
@@ -13,12 +70,82 @@ export function initialState(conversationId, messages = [], application = {}) {
|
|
|
13
70
|
|
|
14
71
|
export function reduceChat(state, event) {
|
|
15
72
|
if (event?.conversation_id !== state.conversationId) return state;
|
|
73
|
+
const structured = state.protocolVersion === 1;
|
|
74
|
+
if (
|
|
75
|
+
structured &&
|
|
76
|
+
(event.protocolVersion !== 1 ||
|
|
77
|
+
!sameChatScope(state.scope, event.scope) ||
|
|
78
|
+
state.scope.conversationId !== state.conversationId ||
|
|
79
|
+
!revision(state.instanceGeneration) ||
|
|
80
|
+
event.instanceGeneration !== state.instanceGeneration)
|
|
81
|
+
)
|
|
82
|
+
return state;
|
|
16
83
|
if (["chat.snapshot", "chat.reset"].includes(event.type)) {
|
|
84
|
+
if (structured && !revision(event.composer?.revision))
|
|
85
|
+
return requestSync(state);
|
|
86
|
+
const currentRevision = state.composer?.revision ?? -1;
|
|
87
|
+
if (structured && event.composer.revision < currentRevision) return state;
|
|
88
|
+
if (structured && !validProjection(event.composer, state))
|
|
89
|
+
return requestSync(state);
|
|
90
|
+
if (structured) {
|
|
91
|
+
const sequences = event.sequences ?? {};
|
|
92
|
+
if (
|
|
93
|
+
typeof sequences !== "object" ||
|
|
94
|
+
Array.isArray(sequences) ||
|
|
95
|
+
!Object.values(sequences).every(revision)
|
|
96
|
+
)
|
|
97
|
+
return requestSync(state);
|
|
98
|
+
// Transcript sequence counters and composer revisions are independent.
|
|
99
|
+
// Only a new explicit reset may discard known counters.
|
|
100
|
+
if (
|
|
101
|
+
(event.type !== "chat.reset" ||
|
|
102
|
+
event.composer.revision === currentRevision) &&
|
|
103
|
+
Object.entries(state.sequences).some(
|
|
104
|
+
([messageId, sequence]) => (sequences[messageId] ?? 0) < sequence,
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
return state;
|
|
108
|
+
}
|
|
109
|
+
const unchanged = structured && event.composer.revision === currentRevision;
|
|
17
110
|
return {
|
|
18
|
-
...initialState(state.conversationId, event.messages || [],
|
|
111
|
+
...initialState(state.conversationId, event.messages || [], {
|
|
112
|
+
...event,
|
|
113
|
+
// Protocol mode and application identity can change only at the hook boundary.
|
|
114
|
+
protocolVersion: state.protocolVersion,
|
|
115
|
+
scope: state.scope,
|
|
116
|
+
instanceGeneration: state.instanceGeneration,
|
|
117
|
+
composer: structured
|
|
118
|
+
? unchanged
|
|
119
|
+
? state.composer
|
|
120
|
+
: nextProjection(event.composer, state)
|
|
121
|
+
: state.composer,
|
|
122
|
+
...(unchanged
|
|
123
|
+
? { runtime: state.runtime, admission: state.admission }
|
|
124
|
+
: {}),
|
|
125
|
+
}),
|
|
19
126
|
sequences: event.sequences || {},
|
|
20
127
|
};
|
|
21
128
|
}
|
|
129
|
+
if (event.type === "chat.composer-state") {
|
|
130
|
+
if (!structured || state.needsSync) return state;
|
|
131
|
+
if (!revision(event.composer?.revision)) return requestSync(state);
|
|
132
|
+
const currentRevision = state.composer?.revision ?? -1;
|
|
133
|
+
if (event.composer.revision <= currentRevision) return state;
|
|
134
|
+
if (
|
|
135
|
+
(event.previousRevision !== undefined &&
|
|
136
|
+
(!revision(event.previousRevision) ||
|
|
137
|
+
event.previousRevision !== currentRevision)) ||
|
|
138
|
+
!validProjection(event.composer, state)
|
|
139
|
+
)
|
|
140
|
+
return requestSync(state);
|
|
141
|
+
return {
|
|
142
|
+
...state,
|
|
143
|
+
composer: nextProjection(event.composer, state),
|
|
144
|
+
metadata: event.metadata ?? state.metadata,
|
|
145
|
+
runtime: event.runtime ?? state.runtime,
|
|
146
|
+
admission: event.admission ?? state.admission,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
22
149
|
if (
|
|
23
150
|
!event.message_id ||
|
|
24
151
|
!Number.isSafeInteger(event.sequence) ||
|
|
@@ -27,7 +154,7 @@ export function reduceChat(state, event) {
|
|
|
27
154
|
return state;
|
|
28
155
|
const last = state.sequences[event.message_id] || 0;
|
|
29
156
|
if (event.sequence <= last) return state;
|
|
30
|
-
if (event.sequence !== last + 1) return
|
|
157
|
+
if (event.sequence !== last + 1) return requestSync(state);
|
|
31
158
|
if (state.needsSync) return state;
|
|
32
159
|
const index = state.messages.findIndex(
|
|
33
160
|
(message) => message.id === event.message_id,
|
|
@@ -40,7 +167,7 @@ export function reduceChat(state, event) {
|
|
|
40
167
|
? [...messages, event.message]
|
|
41
168
|
: messages.map((m, i) => (i === index ? event.message : m));
|
|
42
169
|
} else {
|
|
43
|
-
if (index < 0) return
|
|
170
|
+
if (index < 0) return requestSync(state);
|
|
44
171
|
const message = messages[index];
|
|
45
172
|
let next;
|
|
46
173
|
switch (event.type) {
|
|
@@ -83,8 +210,10 @@ export function reduceChat(state, event) {
|
|
|
83
210
|
...state,
|
|
84
211
|
messages,
|
|
85
212
|
metadata: event.metadata ?? state.metadata,
|
|
86
|
-
runtime: event.runtime ?? state.runtime,
|
|
87
|
-
admission:
|
|
213
|
+
runtime: structured ? state.runtime : (event.runtime ?? state.runtime),
|
|
214
|
+
admission: structured
|
|
215
|
+
? state.admission
|
|
216
|
+
: (event.admission ?? state.admission),
|
|
88
217
|
sequences: { ...state.sequences, [event.message_id]: event.sequence },
|
|
89
218
|
};
|
|
90
219
|
}
|
|
@@ -71,7 +71,7 @@ export const DuskmoonReactForm = {
|
|
|
71
71
|
nativeControls() {
|
|
72
72
|
const managedPaths = this.fields.map((element) => pathOf(parseJSON(element.dataset.dmReactField).name).map(String));
|
|
73
73
|
return [...this.form.querySelectorAll("input[name], select[name], textarea[name]")].filter((element) => {
|
|
74
|
-
if (element.closest("[data-dm-react-field]")) return false;
|
|
74
|
+
if (element.matches(":disabled") || element.closest("[data-dm-react-field]")) return false;
|
|
75
75
|
const path = pathOf(element.name).map(String);
|
|
76
76
|
return !managedPaths.some((managed) => managed.slice(0, Math.min(path.length, managed.length)).every((key, index) => key === path[index]));
|
|
77
77
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "phoenix_duskmoon",
|
|
3
|
-
"version": "10.0
|
|
3
|
+
"version": "10.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./priv/static/assets/js/phoenix_duskmoon.js",
|
|
@@ -37,20 +37,34 @@
|
|
|
37
37
|
"registry": "https://registry.npmjs.org/"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@duskmoon-dev/art-elements": "1.
|
|
41
|
-
"@duskmoon-dev/components": "0.
|
|
42
|
-
"@duskmoon-dev/core": "1.
|
|
43
|
-
"@duskmoon-dev/css-art": "1.
|
|
44
|
-
"@duskmoon-dev/elements": "1.
|
|
45
|
-
"react": "19.3.
|
|
46
|
-
"react-dom": "19.3.
|
|
40
|
+
"@duskmoon-dev/art-elements": "1.10.2",
|
|
41
|
+
"@duskmoon-dev/components": "0.6.0",
|
|
42
|
+
"@duskmoon-dev/core": "1.21.0",
|
|
43
|
+
"@duskmoon-dev/css-art": "1.21.0",
|
|
44
|
+
"@duskmoon-dev/elements": "1.10.2",
|
|
45
|
+
"react": "19.3.1",
|
|
46
|
+
"react-dom": "19.3.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@mdi/svg": "7.4.47",
|
|
50
50
|
"bootstrap-icons": "1.13.2",
|
|
51
|
-
"postcss": "8.5.
|
|
51
|
+
"postcss": "8.5.32"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"tailwindcss": "^4.3.2"
|
|
54
|
+
"tailwindcss": "^4.3.2",
|
|
55
|
+
"lexical": "0.53.0",
|
|
56
|
+
"@lexical/history": "0.53.0",
|
|
57
|
+
"@lexical/plain-text": "0.53.0"
|
|
58
|
+
},
|
|
59
|
+
"peerDependenciesMeta": {
|
|
60
|
+
"lexical": {
|
|
61
|
+
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"@lexical/history": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"@lexical/plain-text": {
|
|
67
|
+
"optional": true
|
|
68
|
+
}
|
|
55
69
|
}
|
|
56
70
|
}
|