True, and even if IA wants to squash it, I don't see why they couldn't/wouldn't still provide an administrative mechanism for enabling it, along with a strong caution disclaimer.
if you are looking for more beneficial engagement then I suggest creating a module for those examples.
first of off like you have said
ask yourself if you would feel comfortable downloading someone's code that contains code similar to malware? I mean even if the people on here knew and trusted you it's still sketchy.
my solution to the indention and escape characters is to just make a webdev mounted file and attach it to the perspective page using a markdown injection but, I understand not everyone has the webdev module.
secondly markdown injections are dying. ignition hasn't set it in stone, but they have said
which makes me strongly believe they plan on just full on killing it at some point and I don't think there will be an alternative natively until much later.
I would suggest having a plan for if they do remove script parsing from markdown injections. Thus far it seems custom modules are the only guaranteed supported way to add JS
Already been working on a module as my time allows: GitHub - EckmanTechLLC/ignite · GitHub
The cookbook was just exploration, and this thread is titled "JS Injection Hack Usefuls (JavaScript)" so I thought my posts are on topic. I did consider the webdev module route - which 2 of the cookbook recipes do use it, but still defaulted to the base64 for ease and quick results/trials.
You are right - I did see that from Kyle Chase, which means I'll update the cookbook readme to be more accurate about the supportability of "exploration" ideas and techniques.
Do you have an example command? I think I understand, but I’ve never done this before.
We need someone to JS Inject a rich text editor that is actually worth it. Like ToastUI
That is a module we would pay for....
Got a link? I can almost guarantee you can load this in Periscope.
I started working on making my Markdown+ Editor a true WYSIWYG rich text editor when I discovered how awesome Claude is at JavaScript, but got sidetracked by using him to make games instead. I thought, wouldn't it be cool to include a little Tetris Easter egg in it, and soon found myself building something a bit more substantial...
I really need to browse the exchange more often.
Will test it out!
Loaded from a CDN, I'm still mulling over how to handle external libraries within Periscope's module resolution system.
() => { document.documentElement.classList.toggle('dark'); }
Comparison shown below comparing switching theme via session prop (purple icon) and switching via the above.
Note: the purple switch is broken because I deleted the old dark theme, but it still demonstrates what was happening when it was there with the brief unstyled page.
Essentially, you need to define your default light-themed CSS variables and then override them within a .dark-scoped selector so the values change when dark mode is active with the js toggle
/* Default (light theme) */
:root {
--colour-pg-bg: hsl(214 30% 91%);
}
/* Dark theme override (triggered by class on <html>) */
html.dark {
--colour-pg-bg: hsl(216 14% 27%);
}
What I had to do also however was to include the IA "dark" theme under a different name within my theme (import it), however I edited the selectors within the css files by adding .dark to the front of each, e.g.
Got it.
Client Resource: InjectionHacks/Theme
import { context } from "system:perspective";
const THEME_KEY = "theme";
const DARK_CLASS = "dark";
const syncSessionTheme = () => {
const isDark = document.documentElement.classList.contains(DARK_CLASS);
const themeValue = isDark ? "dark" : "light";
context.page.sessionCustom.write(THEME_KEY, themeValue);
};
export const setTheme = (isDark) => {
document.documentElement.classList.toggle(DARK_CLASS, isDark);
syncSessionTheme();
};
export const toggleTheme = () => {
document.documentElement.classList.toggle(DARK_CLASS);
syncSessionTheme();
};
export const initThemeListener = () => {
syncSessionTheme();
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === "class") {
syncSessionTheme();
}
});
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["class"],
});
return () => observer.disconnect();
};
initThemeListener();
Button Script:
def runAction(self, event):
system.perspective.runJavaScriptAsync('''async () => {
const theme = await periscope.resource('InjectionHacks/Theme')
theme.toggleTheme()
}''')
Notice that you can access client resources by name via periscope.resource(name).
In this example, the label is bound to session.custom.theme and the background of the container is --colour-pg-bg.
Also, you don't really need to use an observer. You could easily just have setTheme write directly to the session custom property.
Would I be able to use this to provide an external handler/plugin for the chartjs that's part of your embr charts module?
Yup, that’s actually my primary use case.
The current plan is to introduce a propsTransformers property on all Embr components.
- This property will apply a list of functions to the component’s
propsbefore rendering. - Using this, you can add your plugins (written as Client Resources) to the chart.
The exact implementation may change, but 100% there will be some method for using Client Resources as Chart.js plugins.
It actually might even be possible now using await periscope.resource(path)?
The biggest hurdle is that even though Client Resources are cached hard on the client, getting a reference to them still has to be asynchronous to ensure they are finished loading.
(Because I can’t provide a resource manifest in the initial Page HTML, I have to do an HTTP request to get it.)
Which basically means any components that use Client Resources need to be wrapped in React ‘lazy` to delay rendering until loading is finished. It’s the only way to do things, without heavily hacking into how Perspective starts a client.


