Coder Social home page Coder Social logo

gjsify / gnome-shell Goto Github PK

View Code? Open in Web Editor NEW
48.0 5.0 6.0 4.39 MB

GJS TypeScript type definitions for GNOME Shell Extensions

License: MIT License

JavaScript 92.64% TypeScript 7.36%
gir gjs gnome gobject-introspection shell types typescript gnomeshell extension

gnome-shell's Introduction

Gjsify

Combine the power of Typescript with the power of GJS

Development

Dependencies

Fedora:

sudo dnf install meson vala gjs

Ubuntu:

sudo apt install meson valac gjs

Build

NODE_OPTIONS=--max_old_space_size=9216 yarn run build

gnome-shell's People

Contributors

flexagoon avatar jp-vernooy avatar jumplink avatar piousdeer avatar schnz avatar swsnr avatar totto16 avatar zeroxdead avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

gnome-shell's Issues

When constructing `St.Widget`, specifying `layout_manager` should be reflected in types

When one constructs a St.Widget like this:

const myWidget = new St.Widget({
  layout_manager: new Clutter.GridLayout(),
})

The type of myWidget.layout_manager is still LayoutManager, which would make TypeScript errors when one tries to use methods from GridLayout.

Suggestion 1: Multiple type parameters

// St
class Widget<LayoutManagerT extends Clutter.LayoutManager | null = null, LabelActorT extends Clutter.LabelActor | null = null> extends Clutter.Actor<LayoutManagerT, LabelActorT> {
  constructor(config?: Widget.ConstructorProperties<LayoutManagerT, LabelActorT>)
}

// Clutter
interface Actor<LayoutManagerT extends LayoutManager | null, LabelActorT extends LabelActor | null> {
  layout_manager: LayoutManagerT
  layoutManager: LayoutManagerT
  label_actor: LabelActorT
  labelActor: LabelActorT
}

WARNING: ConstructorProperties now shouldn't be a single interface, but a union of snake_case properties and camelCase properties. Keeping it as a single interface would have required user to specify both layout_manager and layoutManager, which would be absurd.

Suggestion 2: Type dictionary as a single type parameter

// St
class Widget<Config extends Widget.ConstructorProperties | null> extends ClutterActor<Config> {
  constructor(config: Config)
}

// Clutter
interface Actor<Config extends Widget.ConstructorProperties | null> {
  layout_manager: Config extends Widget.ConstructorProperties ? Config['layout_manager'] : null
  layoutManager: Config extends Widget.ConstructorProperties ? Config['layoutManager'] : null
  label_actor: Config extends Widget.ConstructorProperties ? Config['label_actor'] : null
  labelActor: Config extends Widget.ConstructorProperties ? Config['labelActor'] : null
}

NOTE: I strongly recommend this solution. ConstructorProperties does not need to change at all.

Usage without bundling?

I'm trying to replace my hand-written GNOME Shell types with this repository, but no matter what combination of module and moduleResolution I try, I can't get these types to work.

Specifically, if I set module and moduleResolution to Node16 in tsconfig.json, I get errors like these from the type definitions on tsc --build tsconfig.json:

node_modules/@girs/gnome-shell/dist/misc/extensionUtils.d.ts:3:32 - error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean '../extensions/extension.js'?

3 import type { Extension } from "../extensions/extension";

I can suppress these with "skipLibCheck": true, but it makes the type definitions unusable because none of the relative imports get resolved, so typescript is missing proper type info all around.

I don't really understand what I'm doing wrong, and the documentation is somewhat lacking. All examples (here, as well as in the gjsify/types repo) seem to use some sort of bundling, and there seem to be no instructions around just using the types for tsc without any kind of bundling.


Besides, I looked at the example to understand how it does things, and as far as I understand it uses esbuild to bundle all TS into a single JS file as you'd to for web. I looked at the generated JS and that's the convoluted unreadable mess you typically expect from a bundler.

I don't understand how this is supposed to work with extensions.gnome.org whose review guidelines explicitly request non-obfuscated code even after typescript compilation. In a review of my extension I was actually ask to add a prettification step as post-processing to make the code more readable, even though I just submitted the tsc output literally without any bundling.

So I can't image that a bundled extension file with output similar to what esbuild creates in the example would pass review on extensions.gnome.org. Did anyone actually ever submit a bundled extension?

Edit: A previous version of this text had an entirely unrelated error which was a result from messing around too much ๐Ÿ™ˆ

[question] Cannot import Gtk using const { Gtk } = imports.gi;

Hey, this is my first interaction with gnome-shell extension and typescript.
My learning consists of modifying an example from this repo.
However, I can't import the GtK library using:

const { Gtk } = imports.gi;

I got error "Property 'Gtk' does not exist on type 'GjsGiImports'.ts(2339)"
I also tried to use import

import  { Gtk } from '@girs/gtk-4.0';

but it didn't like probably gnome-shell after I got an error: ReferenceError: require is not defined

I don't know what the problem is, am I making some kind of mistake ?

Suggestion: move away from Pre-Release to Release

Since using gir dependencies x.x.x-4.0.0-beta.y we had no new issues / bugs reported.
I also tested them and they seem to be working correct (after fixing some things in #34)

So I suggest updating the version to a non 46.0.0-4.0.0-beta.y one.

If we should call it 46.0.0-beta7 or 46.1.0-beta1 or something similar, is up to you.
I personally prefer incrementing the minor version number (hence 46.1.0) but ultimately I don't care, as long it's visible, that it's not a beta anymore.

Maybe we should wait, that the upstream gir dependencies are out of their 4.0.0-beta too ๐Ÿคท๐Ÿผโ€โ™‚๏ธ

Anyway, this issue is here, to publicly discuss this.

CC @JumpLink @swsnr

Extension.metadata is missing

In the gjs extension guide, there is the following code:

const Me = ExtensionUtils.getCurrentExtension();
// ...
let indicatorName = `${Me.metadata.name} Indicator`;

In the types from this repository, the result of ExtensionUtils.getCurrentExtension() is an object conforming to the Extension interface, which does not have a metadata property:

export interface Extension {
    type: ExtensionType;
    state: ExtensionState;
    path: string;
    error: string | null;
    hasPrefs: boolean;
    hasUpdate: boolean;
    canChange: boolean;
}

Gnome 45: How to move forward?

Preamble
I am currently in the process of rewriting a Gnome shell extension and the breaking changes of Gnome 45 were the trigger for this. I am not the author of the original extension and I am a total newbie on the subject, although I learned alot within the last few days.

The typings you provide here (and here) are very useful for a type safe and productive development. I wonder how anyone could write a complex extension without them. However, they are currently targeting Gnome 44 from what I can tell.

Matter at hand
I have used ts-for-gir to create Gnome 45 compatible typings and also created a package for the gnome-shell essentially by forking this repository and performing a search replace for the imports (adapting the version= query). And.... et voilร : I've got phenomenal types for the most part with only a few custom declarations or re-exports required for the gnome-shell package.

I like the idea of distributing an npm package for everyone to use, so I came here. Creating a PR would be a matter of minutes, but I am not really sure what's the best approach in terms of versioning and possible backports. Essentially I see two options:

Option A: Lifecycle Policy
The first option would be to have separate branches such as gnome44, gnome45, etc. Each branch contains the typings for the targeting Gnome environment and of course only depends on @gjsify/<lib> packages that are compatible with this release. This way it would be possible to provide backports whenever typings are improved but at the cost of a higher maintenance overhead. Therefore I propose to put a time limit for backporting of improvements for older Gnome versions (e.g. for 2 years after release).

Option B: Evergreen Policy
The second option would be to simply keep the typings up-to-date with the latest Gnome release and simply publish a new NPM package (ideally with matching versioning) each time. This also means that bugfixes and improvements of types for older Gnome versions wouldn't really be an option (unless they are tracked in separate branches, but then I would go for option A).


I would be happy to go with either of the two options. Maybe there are more options that I am not seeing. I would appreciate feedback from you @JumpLink (or any other contributor/maintainer, if any). Maybe you could also address which approach you would favor for the gjsify/types repository. From what I can tell, Gnome 44 and Gnome 45 packages could live side-by-side from one another as of now. If that is guaranteed to be the case for future releases as well, that would be perfect.

Global types

On startup GNOME Shell creates a few globals and monkey-patches some standard classes, see https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/environment.js

Notably, it

  1. adds a global global = Shell.Global.get(),
  2. adds gettext functions to the global namespace (though using these appears to be discouraged; at least I've never seen anyone using those in extensions),
  3. monkey-patches the standard String to add the format function from imports.Format.format, and
  4. monkey-patches the standard Math object to add a clamp function.

I think these type definitions should mirror those (though 2. is probably debatable), i.e. include these globals in the ambient index module.

I can provide a pull request for these environment types, but I'm unsure where to add a corresponding ambient declaration file, as I haven't really grasped how the final module files are generated by the build scripts ๐Ÿค”

GNOME 46?

Hello,

I'm porting an extension to GNOME 46, and stumbled upon the changes in the notification types. So, now I need to update the typings, but I'm unsure what the intended way forward is with this repo, since presumably more types than just the notification stuff are wrong now.

Should I just make a merge request with whatever changes I have as are needed for my extension?

Or do you intend to go over the whole typings and check them one by one against the current GNOME Shell code?

I'm asking because I can perfectly do the former, but the latter looks like an awful lot of work ๐Ÿ˜ฌ

Template for official CLI

It would be really nice to get the example here out of the box as an option for the template when scaffolding the application with the CLI - gnome-extensions create --template typescript

I looked for some documentation on how to create templates and saw nothing, but it seems that a lot of things surrounding extensions are missing. If you have an idea of where to start looking, I'd be happy to contribute

`_leftBox` and `_rightBox` should exist in `Main.panel`

I don't know if _leftBox and _rightBox exist in all instances of Panel or just Main.panel.

If they exist in all instances of Panel, you can simply add them to Panel interface.

If they are unique to Main.panel, you can do this:

// main.d.ts
export declare const panel: Panel & {
  _leftBox: St.BoxLayout
  _rightBox: St.BoxLayout
}

cannot pass QuickMenuToggle props in constructor/_init

Let's take a look at this sample code:

class MyToggle extends QuickMenuToggle {
    constructor() {
        super({
            title: 'Sample title', // 'title' does not exist in type 'Partial<ConstructorProps>'
            subtitle: 'Blah blah', // 'subtitle' does not exist in type 'Partial<ConstructorProps>'
            iconName: 'selection-mode-symbolic',
        });
        
        this.title = 'Sample title'; // no error
        this.subtitle = 'Blah blah'; // no error
    }
}

Obviously, this happens because those properties are not defined as arguments for constructor/_init. So here is what we can do about it in ui/quickSettings.d.ts:

/**
* Initialization properties of `QuickMenuToggle` class
* -> needs url + version annotation? <-
*/
interface QuickMenuToggleProps { // -> should be exported? <-
    title: string | null;
    subtitle: string | null;
    gicon: Gio.Icon;
    menuEnabled: boolean;
}

/**
 * Class representing a quick menu toggle.
 *
 * @see https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/quickSettings.js#L144
 * @version 46
 */
interface QuickMenuToggle extends QuickMenuToggleProps {} // to make props accessible in class
export declare class QuickMenuToggle extends QuickSettingsItem {
    private _box: St.BoxLayout;
    private _menuButton: St.Button;
  
    /**
     * Initializes a new instance of `QuickMenuToggle`.
     */
    constructor(params: Partial<QuickMenuToggleProps> & Partial<St.Button.ConstructorProps>);

    /**
     * Initializes a new instance of `QuickMenuToggle`.
     */
    _init(params: Partial<QuickMenuToggleProps> & Partial<St.Button.ConstructorProps>): void;
}

With these changes the sample code works just fine:

class MyToggle extends QuickMenuToggle {
    constructor() {
        super({
            title: 'Sample title', // no error
            subtitle: 'Blah blah', // no error
            iconName: 'selection-mode-symbolic',
        });
        
        this.title = 'Other title'; // no error
        this.subtitle = 'beep boop'; // no error
    }
}

So is this:

class MyToggle extends QuickMenuToggle {
    override _init() {
        super._init({
            title: 'Sample title', // no error
            subtitle: 'Blah blah', // no error
            iconName: 'selection-mode-symbolic',
        });
        
        this.title = 'Other title'; // no error
        this.subtitle = 'beep boop'; // no error
    }
}

The same should be done with some other classes. I could make a MR with these changes if you don't mind. Just answer my questions I wrote in the code snippet above

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.