Skip to main content

Vine/Server/Notification/
DecorationTypeLifecycle.rs

1//! Cocoon `window.createTextEditorDecorationType` /
2//! `window.disposeTextEditorDecorationType` notifications. Forwards the
3//! payload on `sky://decoration/<suffix>` as a batch; Sky demultiplexes
4//! back to per-decoration `cel:decoration:*` CustomEvents.
5//!
6//! ~337 create + 317 dispose calls per session. Channel-drain pattern:
7//! a single long-lived flusher wakes on first item, drains, sleeps one
8//! frame (16 ms), drains stragglers, then emits one batched event per
9//! channel name. Zero spawns per call.
10
11use std::sync::{Arc, OnceLock};
12
13use serde_json::{Value, json};
14use tokio::sync::mpsc::{UnboundedSender, unbounded_channel};
15
16use crate::{
17	Host::{RendererEmitter, VineHost},
18	dev_log,
19};
20
21struct DecorationItem {
22	Emitter:Arc<dyn RendererEmitter>,
23
24	Channel:String,
25
26	Payload:Value,
27}
28
29struct DecorationChannel {
30	Sender:UnboundedSender<DecorationItem>,
31}
32
33static DECO_CH:OnceLock<DecorationChannel> = OnceLock::new();
34
35fn GetOrInitChannel() -> &'static DecorationChannel {
36	DECO_CH.get_or_init(|| {
37		let (Tx, mut Rx) = unbounded_channel::<DecorationItem>();
38
39		tokio::spawn(async move {
40			let mut Buf:Vec<DecorationItem> = Vec::with_capacity(64);
41
42			loop {
43				match Rx.recv().await {
44					None => break,
45					Some(Item) => Buf.push(Item),
46				}
47
48				Rx.recv_many(&mut Buf, 4096).await;
49
50				tokio::time::sleep(std::time::Duration::from_millis(16)).await;
51
52				Rx.recv_many(&mut Buf, 4096).await;
53
54				if Buf.is_empty() {
55					continue;
56				}
57
58				let Emitter = Buf[0].Emitter.clone();
59
60				let mut ByChannel:std::collections::HashMap<String, Vec<Value>> = std::collections::HashMap::new();
61
62				for Item in Buf.drain(..) {
63					ByChannel.entry(Item.Channel).or_default().push(Item.Payload);
64				}
65
66				for (ChannelName, Payloads) in ByChannel {
67					let Count = Payloads.len();
68
69					Emitter.Emit(&ChannelName, json!({ "batch": Payloads }));
70
71					dev_log!("sky-emit", "[SkyEmit] ok channel={} batch={}", ChannelName, Count);
72				}
73			}
74		});
75
76		DecorationChannel { Sender:Tx }
77	})
78}
79
80pub async fn DecorationTypeLifecycle(Host:&dyn VineHost, MethodName:&str, Parameter:&Value) {
81	let EventName = format!("sky://decoration/{}", &MethodName["window.".len()..]);
82
83	let Ch = GetOrInitChannel();
84
85	let _ =
86		Ch.Sender
87			.send(DecorationItem { Emitter:Host.RendererEmitter(), Channel:EventName, Payload:Parameter.clone() });
88}