Paste Inputs
thedaviddias/Front-End-Checklist
A skill your agent uses when reviewing rendered HTML, interactive components, or design-system patterns related to Allow pasting into form inputs.
CesiumJS interaction and picking - ScreenSpaceEventHandler, multi-key KeyboardEventModifier input actions, Scene.pick, Scene.drillPick, Scene.pickPosition, Scene.snap edge snapping (1.144), mouse…
$ npx skills add CesiumGS/cesiumjs-skills --skill cesiumjs-interaction -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install CesiumGS/cesiumjs-skills cesiumjs-interaction --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/CesiumGS/cesiumjs-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/cesiumjs-interaction .claude/skills/cesiumjs-interaction && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "cesiumjs-interaction" agent skill from https://github.com/CesiumGS/cesiumjs-skills/tree/main/skills/cesiumjs-interaction into .claude/skills/cesiumjs-interaction/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cesiumjs-interaction", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/CesiumGS/cesiumjs-skills/tree/main/skills/cesiumjs-interactionType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add CesiumGS/cesiumjs-skills --skill cesiumjs-interaction -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install CesiumGS/cesiumjs-skills cesiumjs-interaction --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/CesiumGS/cesiumjs-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/cesiumjs-interaction .agents/skills/cesiumjs-interaction && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "cesiumjs-interaction" agent skill from https://github.com/CesiumGS/cesiumjs-skills/tree/main/skills/cesiumjs-interaction into .agents/skills/cesiumjs-interaction/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cesiumjs-interaction", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add CesiumGS/cesiumjs-skills --skill cesiumjs-interaction -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install CesiumGS/cesiumjs-skills cesiumjs-interaction --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/CesiumGS/cesiumjs-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/cesiumjs-interaction .cursor/skills/cesiumjs-interaction && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "cesiumjs-interaction" agent skill from https://github.com/CesiumGS/cesiumjs-skills/tree/main/skills/cesiumjs-interaction into .cursor/skills/cesiumjs-interaction/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cesiumjs-interaction", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/CesiumGS/cesiumjs-skills.git --path skills/cesiumjs-interaction--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add CesiumGS/cesiumjs-skills --skill cesiumjs-interaction -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install CesiumGS/cesiumjs-skills cesiumjs-interaction --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/CesiumGS/cesiumjs-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/cesiumjs-interaction .gemini/skills/cesiumjs-interaction && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "cesiumjs-interaction" agent skill from https://github.com/CesiumGS/cesiumjs-skills/tree/main/skills/cesiumjs-interaction into .gemini/skills/cesiumjs-interaction/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cesiumjs-interaction", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install CesiumGS/cesiumjs-skills cesiumjs-interactionInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add CesiumGS/cesiumjs-skills --skill cesiumjs-interaction -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/CesiumGS/cesiumjs-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/cesiumjs-interaction .github/skills/cesiumjs-interaction && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "cesiumjs-interaction" agent skill from https://github.com/CesiumGS/cesiumjs-skills/tree/main/skills/cesiumjs-interaction into .github/skills/cesiumjs-interaction/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cesiumjs-interaction", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add CesiumGS/cesiumjs-skills --skill cesiumjs-interaction -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install CesiumGS/cesiumjs-skills cesiumjs-interaction --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/CesiumGS/cesiumjs-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/cesiumjs-interaction .opencode/skills/cesiumjs-interaction && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "cesiumjs-interaction" agent skill from https://github.com/CesiumGS/cesiumjs-skills/tree/main/skills/cesiumjs-interaction into .opencode/skills/cesiumjs-interaction/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cesiumjs-interaction", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
cesiumjs-interactionCesiumJS interaction and picking - ScreenSpaceEventHandler, multi-key KeyboardEventModifier input actions, Scene.pick, Scene.drillPick, Scene.pickPosition, Scene.snap edge snapping (1.144), mouse…
Cesiumjs Interaction is an agent skill from CesiumGS/cesiumjs-skills. CesiumJS interaction and picking - ScreenSpaceEventHandler, multi-key KeyboardEventModifier input actions, Scene.pick, Scene.drillPick, Scene.pickPosition, Scene.snap edge snapping (1.144), mouse and touch events. Use when handling user clicks on the globe, selecting entities or 3D Tiles features, registering modifier-key shortcuts, implementing hover effects, snapping to model edges for measurement, or building drag-based interactions.
Its SKILL.md is about 5.6k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
The repository describes itself as: Curated agent skills for CesiumJS development. The licence is Apache-2.0.
12 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 5f4792c. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are javascript).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Cesiumjs Interaction loads about 5.6k tokens when it runs. Until then it costs about 115 tokens; SKILL.md has 1,148 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from CesiumGS/cesiumjs-skills at commit 5f4792c, republished under its Apache-2.0 licence (© CesiumGS). 1,148 words, ~5,585 tokens.
.claude/skills/cesiumjs-interaction/SKILL.md (or your agent's skills folder).Version baseline: CesiumJS v1.144 (ES module imports, Ion token required).
Central class for mouse, touch, and pointer events on the Cesium canvas.
Always construct a new ScreenSpaceEventHandler bound to viewer.scene.canvas
for interaction logic -- it isolates your listeners from Cesium's default
camera/selection handlers and is the idiomatic pattern shown across all recipes
below.
import { ScreenSpaceEventHandler, ScreenSpaceEventType,
KeyboardEventModifier, defined } from "cesium";
let handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
// Register a click handler
handler.setInputAction((event) => {
console.log("Clicked at", event.position.x, event.position.y);
}, ScreenSpaceEventType.LEFT_CLICK);
// With keyboard modifier (Shift+Click)
handler.setInputAction((event) => {
console.log("Shift+Click at", event.position);
}, ScreenSpaceEventType.LEFT_CLICK, KeyboardEventModifier.SHIFT);
// With multiple modifiers (Ctrl+Shift+Click, 1.142+)
handler.setInputAction((event) => {
console.log("Ctrl+Shift+Click at", event.position);
}, ScreenSpaceEventType.LEFT_CLICK, [
KeyboardEventModifier.CTRL,
KeyboardEventModifier.SHIFT,
]);
// Query or remove actions
const clickAction = handler.getInputAction(ScreenSpaceEventType.LEFT_CLICK);
const ctrlShiftClickAction = handler.getInputAction(ScreenSpaceEventType.LEFT_CLICK, [
KeyboardEventModifier.SHIFT,
KeyboardEventModifier.CTRL, // order does not matter
]);
if (defined(clickAction) && defined(ctrlShiftClickAction)) {
console.log("Click handlers registered");
}
handler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK);
handler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK, [
KeyboardEventModifier.CTRL,
KeyboardEventModifier.SHIFT,
]);
// Always destroy when done to avoid memory leaks
handler = handler && handler.destroy();The Viewer also exposes a built-in handler at viewer.screenSpaceEventHandler
which drives default behavior (entity selection, double-click tracking). You
can attach to it, but for any non-trivial interaction prefer constructing
your own new ScreenSpaceEventHandler(viewer.scene.canvas) so your handlers
are independently disposable and do not collide with Cesium defaults.
| Event | Callback shape | Notes |
|---|---|---|
LEFT_DOWN / LEFT_UP / LEFT_CLICK | ({ position }) | Cartesian2 screen coords |
LEFT_DOUBLE_CLICK | ({ position }) | Left only |
RIGHT_DOWN / RIGHT_UP / RIGHT_CLICK | ({ position }) | |
MIDDLE_DOWN / MIDDLE_UP / MIDDLE_CLICK | ({ position }) | |
MOUSE_MOVE | ({ startPosition, endPosition }) | Fires on every pointer move |
WHEEL | (delta) | Positive = scroll up |
PINCH_START | ({ position1, position2 }) | Two-finger touch begins |
PINCH_END | () | Two-finger touch ends |
PINCH_MOVE | ({ distance, angleAndHeight }) | Two-finger move |
KeyboardEventModifier: SHIFT, CTRL, ALT -- optional third argument to
setInputAction, getInputAction, and removeInputAction. In 1.142+, pass a
single modifier or an array of modifiers. Modifier arrays are order-independent
but exact: a handler registered for [CTRL, SHIFT] does not fire when ALT is
also held.
import { Cartographic, Math as CesiumMath, defined } from "cesium";
// pick -- synchronous, returns top-most object or undefined
const picked = viewer.scene.pick(event.position);
// pickAsync -- non-blocking (WebGL2, v1.136+), falls back to sync on WebGL1
const picked2 = await viewer.scene.pickAsync(movement.endPosition);
// drillPick -- all objects at position, front-to-back; use limit to cap cost
const allPicked = viewer.scene.drillPick(event.position, 5);
// pickPosition -- world Cartesian3 from depth buffer
if (viewer.scene.pickPositionSupported) {
const cartesian = viewer.scene.pickPosition(event.position);
if (defined(cartesian)) {
const c = Cartographic.fromCartesian(cartesian);
console.log(CesiumMath.toDegrees(c.longitude), CesiumMath.toDegrees(c.latitude), c.height);
}
}Set scene.pickTranslucentDepth = true to include translucent primitives in pickPosition.
// Pick a voxel cell and read its properties
const voxelCell = viewer.scene.pickVoxel(event.position);
if (defined(voxelCell)) {
console.log(voxelCell.getProperty("temperature"));
}scene.snap searches a screen-space region around a window position and
returns the best snap target, preferring model edges (from CAD-style
EXT_mesh_primitive_edge_visibility data) over surfaces; among hits of the
same kind, the one nearest the cursor wins. Use it for measurement and
inspection tools that should latch onto edges instead of raw cursor hits.
import { defined } from "cesium";
// Search a 25x25 px region centered on the cursor
const result = viewer.scene.snap(movement.endPosition, { width: 25 });
if (defined(result)) {
// SceneSnapResult: { object, position, screenPosition, isEdge }
console.log(result.isEdge ? "edge" : "surface", result.position);
}Only primitives rendered through the Model pipeline (3D Tiles and glTF
models) are snappable. Snapping requires WebGL2 with float color attachments
(EXT_color_buffer_float); when that is unsupported, or the region contains
no snappable geometry, snap returns undefined.
| Picked object | Return shape | Key properties |
|---|---|---|
| Entity | { primitive, id } | id is the Entity instance |
| Cesium3DTileFeature | Cesium3DTileFeature | .getProperty(name), .getPropertyIds(), .color |
| Billboard/Label (collection) | { primitive, id } | id is the user-set id |
| Primitive (geometry) | { primitive, id } | id is the GeometryInstance id |
| Globe surface | undefined | Use camera.pickEllipsoid() or pickPosition() |
Picking and hover demos must render the subject geography, not the starfield. Recent losses occurred because the camera was angled at the sky (pitch ~-45° or shallower) or imagery failed to load, leaving the scene black. Two non-negotiable rules:
viewer.camera.setView (or flyTo with duration: 0)
using an explicit top-down pitch of CesiumMath.toRadians(-90) (nadir)
when the scenario calls for a regional map view. Do not rely on default
pitch.import { Cartesian3, Math as CesiumMath } from "cesium";
viewer.camera.setView({
destination: Cartesian3.fromDegrees(-122.0, 37.5, 1_500_000),
orientation: { heading: 0.0, pitch: CesiumMath.toRadians(-90), roll: 0.0 },
});Whenever you display longitude or latitude to the user (label text, console
log, HTML overlay), convert from radians to degrees with
CesiumMath.toDegrees. Cartographic angles are always in radians; printing
them raw produces nonsense values.
import { Cartographic, Math as CesiumMath } from "cesium";
const c = Cartographic.fromCartesian(cartesian);
const lonDeg = CesiumMath.toDegrees(c.longitude);
const latDeg = CesiumMath.toDegrees(c.latitude);
// Never display c.longitude / c.latitude directly in a UI label.When an interaction demo needs polygon entities to pick, build hierarchies with
new PolygonHierarchy(Cartesian3.fromDegreesArray([...])). PolygonHierarchy
does not have static fromDegrees(), fromDegreesArray(), or
fromEquatorialCoordinates() helpers -- calling them throws
Cesium.PolygonHierarchy.fromDegrees is not a function at runtime and the
scene renders empty. The only correct construction is:
import { PolygonHierarchy, Cartesian3 } from "cesium";
const hierarchy = new PolygonHierarchy(
Cartesian3.fromDegreesArray([
-87.6, 41.8,
-85.6, 41.8,
-85.6, 43.3,
-87.6, 43.3,
]),
);
viewer.entities.add({
polygon: { hierarchy, material: Color.CRIMSON.withAlpha(0.5) },
});const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction((event) => {
const picked = viewer.scene.pick(event.position);
if (defined(picked) && defined(picked.id)) {
viewer.selectedEntity = picked.id; // shows InfoBox
} else {
viewer.selectedEntity = undefined;
}
}, ScreenSpaceEventType.LEFT_CLICK);import { Cesium3DTileFeature, Color } from "cesium";
const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction((event) => {
const picked = viewer.scene.pick(event.position);
if (picked instanceof Cesium3DTileFeature) {
// Read properties
const ids = picked.getPropertyIds();
ids.forEach((id) => console.log(`${id}: ${picked.getProperty(id)}`));
picked.color = Color.YELLOW; // highlight
}
}, ScreenSpaceEventType.LEFT_CLICK);const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction((event) => {
const cartesian = viewer.camera.pickEllipsoid(
event.position, viewer.scene.globe.ellipsoid);
if (defined(cartesian)) {
const c = Cartographic.fromCartesian(cartesian);
console.log(`Lon: ${CesiumMath.toDegrees(c.longitude).toFixed(6)}`);
console.log(`Lat: ${CesiumMath.toDegrees(c.latitude).toFixed(6)}`);
}
}, ScreenSpaceEventType.LEFT_CLICK);For height on 3D content, use scene.pickPosition instead (see above).
import { EntityCollection, CallbackProperty, ColorMaterialProperty, Color } from "cesium";
const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
const pickedEntities = new EntityCollection();
const highlightColor = Color.YELLOW.withAlpha(0.5);
// Make entity material react to selection state
function makePickable(entity, baseColor) {
entity.polygon.material = new ColorMaterialProperty(
new CallbackProperty((time, result) => {
return pickedEntities.contains(entity)
? highlightColor.clone(result) : baseColor.clone(result);
}, false));
}
handler.setInputAction((movement) => {
const all = viewer.scene.drillPick(movement.endPosition);
pickedEntities.removeAll();
for (const p of all) {
if (defined(p.id)) pickedEntities.add(p.id);
}
}, ScreenSpaceEventType.MOUSE_MOVE);import { Color } from "cesium";
const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
const highlighted = { feature: undefined, originalColor: new Color() };
handler.setInputAction((movement) => {
if (defined(highlighted.feature)) {
highlighted.feature.color = highlighted.originalColor;
highlighted.feature = undefined;
}
const picked = viewer.scene.pick(movement.endPosition);
if (defined(picked) && defined(picked.color)) {
highlighted.feature = picked;
Color.clone(picked.color, highlighted.originalColor);
picked.color = Color.YELLOW;
}
}, ScreenSpaceEventType.MOUSE_MOVE);For entities, the picked object is picked.id; swap the graphics material
instead of picked.color (e.g. store picked.id.polygon.material and set it
to Color.YELLOW, restoring the previous entity's material first). Build the
graphics type the prompt names: a "polygon over a region" means polygon
graphics (with PolygonHierarchy positions), not rectangle graphics, even
when the region is rectangular.
import { Cartographic, EllipsoidGeodesic, Ellipsoid, Color } from "cesium";
const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
const positions = [];
handler.setInputAction((event) => {
const cartesian = viewer.camera.pickEllipsoid(
event.position, viewer.scene.globe.ellipsoid);
if (!defined(cartesian)) return;
positions.push(cartesian);
if (positions.length === 2) {
viewer.entities.add({
polyline: { positions: positions.slice(), width: 3,
material: Color.RED, clampToGround: true },
});
const start = Cartographic.fromCartesian(positions[0]);
const end = Cartographic.fromCartesian(positions[1]);
const geodesic = new EllipsoidGeodesic(start, end, Ellipsoid.WGS84);
console.log(`Distance: ${(geodesic.surfaceDistance / 1000).toFixed(2)} km`);
positions.length = 0;
}
}, ScreenSpaceEventType.LEFT_CLICK);Initialize the label with a sensible starting value so it is visible
before any pointer movement -- scenarios often assert a default readout
without user interaction. Always convert via CesiumMath.toDegrees.
import { HorizontalOrigin, VerticalOrigin, Cartesian2, Cartesian3,
Cartographic, Math as CesiumMath } from "cesium";
const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
// Seed position/text so the label renders immediately on load.
const initialLon = 25.0;
const initialLat = 37.5;
const coordLabel = viewer.entities.add({
position: Cartesian3.fromDegrees(initialLon, initialLat),
label: {
text: `Lon: ${initialLon.toFixed(2)} Lat: ${initialLat.toFixed(2)}`,
show: true, showBackground: true, font: "14px monospace",
horizontalOrigin: HorizontalOrigin.LEFT,
verticalOrigin: VerticalOrigin.TOP,
pixelOffset: new Cartesian2(15, 0),
},
});
handler.setInputAction((movement) => {
const cartesian = viewer.camera.pickEllipsoid(
movement.endPosition, viewer.scene.globe.ellipsoid);
if (defined(cartesian)) {
const c = Cartographic.fromCartesian(cartesian);
coordLabel.position = cartesian;
coordLabel.label.show = true;
coordLabel.label.text =
`Lon: ${CesiumMath.toDegrees(c.longitude).toFixed(4)}\n` +
`Lat: ${CesiumMath.toDegrees(c.latitude).toFixed(4)}`;
}
}, ScreenSpaceEventType.MOUSE_MOVE);import { Cesium3DTileFeature } from "cesium";
const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction((event) => {
const picked = viewer.scene.pick(event.position);
if (!defined(picked)) {
console.log("No object picked");
} else if (picked instanceof Cesium3DTileFeature) {
console.log("3D Tile feature:", picked.getProperty("name"));
} else if (defined(picked.id) && defined(picked.id.position)) {
viewer.selectedEntity = picked.id; // Entity
} else if (defined(picked.primitive)) {
console.log("Primitive:", picked.primitive.constructor.name);
}
}, ScreenSpaceEventType.LEFT_CLICK);const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
const highlighted = { feature: undefined, originalColor: new Color() };
handler.setInputAction(async (movement) => {
if (defined(highlighted.feature)) {
highlighted.feature.color = highlighted.originalColor;
highlighted.feature = undefined;
}
const picked = await viewer.scene.pickAsync(movement.endPosition);
if (defined(picked) && defined(picked.color)) {
highlighted.feature = picked;
Color.clone(picked.color, highlighted.originalColor);
picked.color = Color.YELLOW;
}
}, ScreenSpaceEventType.MOUSE_MOVE);Silhouettes are a post-process edge-detection effect applied to selected primitives. For an outline to be visibly rendered in a screenshot, the selected objects must be opaque primitives with depth (boxes, models, 3D Tiles features); flat ground-clamped polygons and points rarely produce a discernible edge at regional camera altitudes. Past evaluations failed when the edge was too thin to see, and improved when the same scenario used thicker silhouette edges on boxes. To avoid that regression:
box graphics with non-zero dimensions (typically 10–50 km per
side at regional camera altitudes) or 3D Tiles features over point or
ground polygons.length of at least 0.25, and bump to 0.5 for
city-scale or wider framings. Values below 0.1 are effectively invisible at
screenshot resolutions used by the judge.Color.ORANGE, Color.YELLOW, Color.LIME); avoid blues which blend
into ocean tiles.import { PostProcessStageLibrary, Color } from "cesium";
const scene = viewer.scene;
const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
const silhouetteHover = PostProcessStageLibrary.createEdgeDetectionStage();
silhouetteHover.uniforms.color = Color.YELLOW;
silhouetteHover.uniforms.length = 0.25;
silhouetteHover.selected = [];
const silhouetteSelect = PostProcessStageLibrary.createEdgeDetectionStage();
silhouetteSelect.uniforms.color = Color.ORANGE;
silhouetteSelect.uniforms.length = 0.5;
silhouetteSelect.selected = [];
scene.postProcessStages.add(
PostProcessStageLibrary.createSilhouetteStage([silhouetteHover, silhouetteSelect]));
let selectedFeature;
handler.setInputAction((movement) => {
silhouetteHover.selected = [];
const picked = scene.pick(movement.endPosition);
if (defined(picked) && picked !== selectedFeature) {
silhouetteHover.selected = [picked];
}
}, ScreenSpaceEventType.MOUSE_MOVE);
handler.setInputAction((event) => {
silhouetteSelect.selected = [];
const picked = scene.pick(event.position);
if (defined(picked)) {
selectedFeature = picked;
silhouetteSelect.selected = [picked];
silhouetteHover.selected = [];
} else {
selectedFeature = undefined;
}
}, ScreenSpaceEventType.LEFT_CLICK);For scenarios that pre-select an entity at load time (no user interaction
expected), assign silhouetteSelect.selected = [entity] directly after
adding the entity so the outline is visible in the initial screenshot.
const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction((delta) => {
// delta > 0 = scroll up (zoom in), delta < 0 = scroll out
const zoomAmount = delta > 0 ? 0.9 : 1.1;
viewer.camera.zoomIn(viewer.camera.positionCartographic.height * (1 - zoomAmount));
}, ScreenSpaceEventType.WHEEL);viewer.scene.canvas.addEventListener("contextmenu", (e) => e.preventDefault());
const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction((event) => {
const picked = viewer.scene.pick(event.position);
if (defined(picked) && defined(picked.id)) {
showContextMenu(event.position, picked.id); // your app logic
}
}, ScreenSpaceEventType.RIGHT_CLICK);const handler = new ScreenSpaceEventHandler(viewer.scene.canvas);
let draggedEntity = null;
const sscc = viewer.scene.screenSpaceCameraController;
handler.setInputAction((event) => {
const picked = viewer.scene.pick(event.position);
if (defined(picked) && defined(picked.id)) {
draggedEntity = picked.id;
sscc.enableRotate = false;
sscc.enableTranslate = false;
}
}, ScreenSpaceEventType.LEFT_DOWN);
handler.setInputAction((movement) => {
if (!defined(draggedEntity)) return;
const cartesian = viewer.camera.pickEllipsoid(
movement.endPosition, viewer.scene.globe.ellipsoid);
if (defined(cartesian)) draggedEntity.position = cartesian;
}, ScreenSpaceEventType.MOUSE_MOVE);
handler.setInputAction(() => {
draggedEntity = null;
sscc.enableRotate = true;
sscc.enableTranslate = true;
}, ScreenSpaceEventType.LEFT_UP);pickAsync over pick on MOUSE_MOVE -- synchronous pick stalls the GPU pipeline; pickAsync yields to the GPU and resolves next frame (WebGL2, v1.136+).drillPick with a limit -- without one, it re-renders the scene for every overlapping object.pick in MOUSE_MOVE when only click picking is needed -- MOUSE_MOVE fires on every pointer move and triggers a pick render pass each time.depthTestAgainstTerrain for accurate pickPosition results over terrain.scene.pickPositionSupported before using pickPosition -- falls back to camera.pickEllipsoid on unsupported GPUs.scene.pickTranslucentDepth = true only when needed -- adds an extra render pass.Cartesian3 to pickPosition to avoid GC pressure in MOUSE_MOVE.scene.requestRenderMode = true with picking to avoid unnecessary renders; call scene.requestRender() only on state changes.© CesiumGS, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/cesiumjs-interaction of CesiumGS/cesiumjs-skills.
Open the folder on GitHubat commit 5f4792c
Cesiumjs Interaction next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Cesiumjs Interaction this skillCesiumGS/cesiumjs-skills | 189 | — | ~5.6k | Automated safety check: Pass | Apache-2.0 | |
| Paste Inputsthedaviddias/Front-End-Checklist | 74k | — | ~443 | Automated safety check: Pass | MIT | |
| Search Inputthedaviddias/Front-End-Checklist | 74k | — | ~402 | Automated safety check: Pass | MIT | |
| Input Typesthedaviddias/Front-End-Checklist | 74k | — | ~487 | Automated safety check: Pass | MIT | |
| Remotion Interactivityremotion-dev/remotion | 62k | 5 repos | ~4.8k | Automated safety check: Pass | Custom licence | |
| Firecrawl Interact Integrationfirecrawl/firecrawl | 190k | 1 repos | ~731 | Automated safety check: Pass | ISC |
thedaviddias/Front-End-Checklist
A skill your agent uses when reviewing rendered HTML, interactive components, or design-system patterns related to Allow pasting into form inputs.
thedaviddias/Front-End-Checklist
A skill your agent uses when reviewing templates, rendered HTML, or shared components related to Make search inputs accessible.
thedaviddias/Front-End-Checklist
A skill your agent uses when reviewing templates, rendered HTML, or shared components related to Use semantic input type attributes.
remotion-dev/remotion
Structure Remotion markup for interactivity. An agent skill from remotion-dev/remotion.
firecrawl/firecrawl
Guides adding Firecrawl's /interact endpoint to product code for pages that need clicks, forms, pagination or logged-in flows beyond plain scraping.
flutter/flutter
How to land a formal cherry-pick of a merged PR for the flutter/flutter repo stable or beta channel.
CesiumGS/cesiumjs-skills
CesiumJS 3D Tiles - Cesium3DTileset, compressed and CAD-style glTF content, MVTDataProvider, UrlTemplate3DTilesDataProvider, styling, metadata, feature picking, voxels, point clouds, I3S, Gaussian…
CesiumGS/cesiumjs-skills
CesiumJS core utilities and networking - Resource, Color, Event, Request, RequestScheduler, error handling, helper functions, feature detection.
CesiumGS/cesiumjs-skills
CesiumJS materials and post-processing — Material, Fabric JSON, MaterialAppearance, ImageBasedLighting, PostProcessStage, PostProcessStageLibrary, bloom, depth of field, ambient occlusion, FXAA…
CesiumGS/cesiumjs-skills
CesiumJS spatial math - Cartesian3, Cartographic, Matrix4, Quaternion, Transforms, Ellipsoid, BoundingSphere, projections, coordinate conversions.
CesiumGS/cesiumjs-skills
CesiumJS terrain, globe, and environment - TerrainProvider, Globe, sampleTerrain, atmosphere, sky, fog, lighting, shadows, panoramas.
CesiumGS/cesiumjs-skills
CesiumJS viewer setup - Viewer, CesiumWidget, widgets, Ion token, Scene configuration, SceneMode, factory helpers, geocoders, platform services.
CesiumJS interaction and picking - ScreenSpaceEventHandler, multi-key KeyboardEventModifier input actions, Scene.pick, Scene.drillPick, Scene.pickPosition, Scene.snap edge snapping (1.144), mouse…. Cesiumjs Interaction is an agent skill from CesiumGS/cesiumjs-skills.144), mouse and touch events.
Cesiumjs Interaction fits situations like: handling user clicks on the globe; selecting entities; 3D Tiles features; registering modifier-key shortcuts.
Run `npx skills add CesiumGS/cesiumjs-skills --skill cesiumjs-interaction -a claude-code`. Or copy the skill folder (skills/cesiumjs-interaction in CesiumGS/cesiumjs-skills) into .claude/skills/cesiumjs-interaction in your project. Claude Code loads it when a task matches its description.
Run `npx skills add CesiumGS/cesiumjs-skills --skill cesiumjs-interaction -a codex`. Or copy the skill folder (skills/cesiumjs-interaction in CesiumGS/cesiumjs-skills) into .agents/skills/cesiumjs-interaction in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add CesiumGS/cesiumjs-skills --skill cesiumjs-interaction -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/cesiumjs-interaction, .gemini/skills/cesiumjs-interaction, .github/skills/cesiumjs-interaction and .opencode/skills/cesiumjs-interaction in your project.
SKILL.md names no scripts, command-line tools or credentials: Cesiumjs Interaction is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Cesiumjs Interaction is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5.6k tokens (SKILL.md is roughly 22k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Cesiumjs Interaction: Paste Inputs (thedaviddias/Front-End-Checklist, 74k stars), Search Input (thedaviddias/Front-End-Checklist, 74k stars), Input Types (thedaviddias/Front-End-Checklist, 74k stars) and Remotion Interactivity (remotion-dev/remotion, 62k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
CesiumGS (a GitHub organization) maintains it in CesiumGS/cesiumjs-skills, which has 189 GitHub stars. The repository holds 14 skills in this directory. The repository was last updated on September 14, 2026.
Source: CesiumGS/cesiumjs-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.