Sudoku review #1

Open
BordedDev wants to merge 19 commits from BordedDev/sudoku:main into main
First-time contributor

Hey, I finally got round to creating a PR (creating a draft until I can add more notes)

  • notes.md
    • These are just things of note I found while looking through the code
  • sudoku.js & sudoku2.html
    • A copy of the original so I can comment on it in the PR (I'm assuming this relatively close to GitHub's PR system)
  • sudoku.modified.js & sudoku2.modified.html
    • These contain some improvements
  • sudoku.rewrite.js & sudoku2.rewrite.html
    • These are a rewrite I was tempted to make as a fun challenge, hope you like 'em
  • sudoku.rewrite.css
    • I wanted to have the css out of the file for editor convenience, of course, it could just as well be in-lined like before to avoid the flash of content
Hey, I finally got round to creating a PR (creating a draft until I can add more notes) - notes.md - These are just things of note I found while looking through the code - sudoku.js & sudoku2.html - A copy of the original so I can comment on it in the PR (I'm assuming this relatively close to GitHub's PR system) - sudoku.modified.js & sudoku2.modified.html - These contain some improvements - sudoku.rewrite.js & sudoku2.rewrite.html - These are a rewrite I was tempted to make as a fun challenge, hope you like 'em - sudoku.rewrite.css - I wanted to have the css out of the file for editor convenience, of course, it could just as well be in-lined like before to avoid the flash of content
BordedDev added 6 commits 2025-01-14 22:24:29 +00:00
BordedDev reviewed 2025-01-15 19:17:00 +00:00
@ -0,0 +18,4 @@
return false
}
class EventHandler {
Author
First-time contributor

A pretty standard, pretty robust way of handling events. I'd lean to the built-in EventTarget until the need for something better is needed.

A pretty standard, pretty robust way of handling events. I'd lean to the built-in [EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) until the need for something better is needed.
BordedDev reviewed 2025-01-15 19:27:07 +00:00
@ -0,0 +237,4 @@
}
}
class Row extends EventHandler {
Author
First-time contributor

The Row/Col classes are confusing in the current situation, since Row isn't used for more than a bag holder/event forwarding

The Row/Col classes are confusing in the current situation, since Row isn't used for more than a bag holder/event forwarding
BordedDev reviewed 2025-01-15 19:29:38 +00:00
@ -0,0 +228,4 @@
}
}
toString() {
Author
First-time contributor

Seeing someone implement a custom toString is always nice

Seeing someone implement a custom `toString` is always nice
BordedDev reviewed 2025-01-15 19:31:19 +00:00
@ -0,0 +287,4 @@
states = []
parsing = false
_initialized = false
initalized = false
Author
First-time contributor

Why are there 2 initialized properties?

Why are there 2 initialized properties?
Owner

Even worse, one is wrongly written :P

Even worse, one is wrongly written :P
BordedDev reviewed 2025-01-15 19:46:19 +00:00
@ -0,0 +678,4 @@
-ms-user-select: none;
background-color: #e5e5e5;
border-radius: 5px;
aspect-ratio: 1/1;
Author
First-time contributor

Unfortunately, this doesn't work with dynamic inside out sizing/nested aspect-ration on firefox. Luckily, you can use em since the size is based on font size

Unfortunately, this doesn't work with dynamic inside out sizing/nested aspect-ration on firefox. Luckily, you can use `em` since the size is based on font size
Owner

So they're not nice squares on firefox? Upgrade to chrome!

So they're not nice squares on firefox? Upgrade to chrome!
Author
First-time contributor

Never! Google can claw my faux-privacy from my cold dead hands (also ublock). I've been considering using chromium/vavaldi so I can get functional HDR (right now I use edge when I need chrome)

Never! Google can claw my faux-privacy from my cold dead hands (also ublock). I've been considering using chromium/vavaldi so I can get functional HDR (right now I use edge when I need chrome)
BordedDev reviewed 2025-01-15 19:48:36 +00:00
@ -0,0 +680,4 @@
border-radius: 5px;
aspect-ratio: 1/1;
}
.sudoku-field-initial {
Author
First-time contributor

You can combine styles as well, I find it to be a bit easier to work with on average since you can toggle and combine them when it's needed much easier.
Also CSS now supports nested styling rules:

.sudoku-field {
		&.initial {
			color: #777;
		}
		&.selected {
			background-color: lightgreen;
		}
		&.marked {
			background-color: blue;
		}
		&.invalid {
			color: red;
		}
}
You can combine styles as well, I find it to be a bit easier to work with on average since you can toggle and combine them when it's needed much easier. Also CSS now supports nested styling rules: ```css .sudoku-field { &.initial { color: #777; } &.selected { background-color: lightgreen; } &.marked { background-color: blue; } &.invalid { color: red; } } ```
Owner

Ah facks. Thanks. Yh, my css skills are very outdated.

Ah facks. Thanks. Yh, my css skills are very outdated.
Author
First-time contributor

Still better than my junior. I was very surprised, learning about the CSS upgrade ~this~ last year

Still better than my junior. I was very surprised, learning about the CSS upgrade ~this~ last year
BordedDev reviewed 2025-01-15 19:51:29 +00:00
@ -0,0 +693,4 @@
color: red;
}
.sudoku-field {
border: 1px solid #ccc;
Author
First-time contributor

Not sure if the double border was intentional, I assume it wasn't.

The way I solved it was making the border on the bottom and right, you can use the nth selector to disable it on the edges if need be

Not sure if the double border was intentional, I assume it wasn't. The way I solved it was making the border on the bottom and right, you can use the nth selector to disable it on the edges if need be
BordedDev reviewed 2025-01-15 19:52:49 +00:00
@ -0,0 +767,4 @@
this.fieldElements = []
this.puzzle = new Puzzle(9)
this.fields = []
this.styleElement = document.createElement('style');
Author
First-time contributor

From what I've read, it's not recommended to create elements in the constructor and instead should be done in the connectedCallback

From what I've read, it's not recommended to create elements in the constructor and instead should be done in the `connectedCallback`
Owner

I'm not sure about that. Will look it up, but you kinda want to have the component ready right when it get 'connected'? I also use that event somewhere I guess.

I'm not sure about that. Will look it up, but you kinda want to have the component ready right when it get 'connected'? I also use that event somewhere I guess.
Author
First-time contributor

It's here: https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements

In the class constructor, you can set up initial state and default values, register event listeners and perhaps create a shadow root. At this point, you should not inspect the element's attributes or children, or add new attributes or children. See Requirements for custom element constructors and reactions for the complete set of requirements.

That's what the connected event is for. They break that "rule" in the example on the same page, and yeah, it gets called through the _bind function. I think the comment was originally meant to be on the _bind call in the constructor

It's here: https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements > In the class constructor, you can set up initial state and default values, register event listeners and perhaps create a shadow root. At this point, you should not inspect the element's attributes or children, or add new attributes or children. See Requirements for custom element constructors and reactions for the complete set of requirements. That's what the connected event is for. They break that "rule" in the example on the same page, and yeah, it gets called through the `_bind` function. I think the comment was originally meant to be on the `_bind` call in the constructor
BordedDev reviewed 2025-01-15 19:56:48 +00:00
@ -0,0 +13,4 @@
}
on(event, listener) {
this.events[event] ||= { name: name, listeners: [], callCount: 0 }
Author
First-time contributor

Simplified the empty event handler assignment. It was meant to be ??= not ||= both will work in this case because of you how JS works though

Simplified the empty event handler assignment. It was meant to be `??=` not `||=` both will work in this case because of you how JS works though
BordedDev reviewed 2025-01-15 19:58:10 +00:00
@ -0,0 +43,4 @@
if (this.suppresEvents) return [];
this.eventCount++;
const returnValue = this.events[event].listeners.map(listener => {
var returnValue = listener(data)
Author
First-time contributor

var :(

var :(
Owner

Oh no! A VAR!

Oh no! A VAR!
BordedDev reviewed 2025-01-15 20:01:01 +00:00
@ -0,0 +348,4 @@
reset() {
this._initialized = false
this.initalized == false;
Author
First-time contributor

Accidental comparison 😅

Accidental comparison 😅
Owner

Oh fuck, that can maybe be the reason something is not working or why even the second variable is implemented. Because I really don't know what the reason was.

Oh fuck, that can maybe be the reason something is not working or why even the second variable is implemented. Because I really don't know what the reason was.
Owner

@BordedDev nice review so far! I will check your new files tomorrow. I'm now fighting with speech recognition and command execution. It's a cool project and it was open source for a while but then i really, really start to rape the code. But check how easy it is to add functions to the LLM:

The docstrings are instructions for the LLM. I have somewhere a class inspector converting that nice to LLM commands.
So, if i need new funcitonality, I just add it there.

class ChatFunctions:

    def __init__(self, gpt):

        self._gpt = gpt

    """Functions callable by GPT"""

    async def uptime(self,*args):
        """
        Execute this function if it has to do with uptime of server or if use wants to know how long a server is up.
        
        :return: str
        """
        result = subprocess.check_output(["ssh","retoor@molodetz.nl","uptime","-p","&&", "printf 'Since:'","&&","uptime","-s"]).decode()
        result = await self._gpt.communicate(f"Explain the uptime of this server using this data: {result}")
        await self._gpt.tts3(result)

    async def deploy(self,*args):
        """
        Execute this function if it has to do with deploy, deployment, update, upgrade

        :return: str
        """
        await self._gpt.tts3("Deploying molodetz.nl")
        response = subprocess.check_output("ssh molodetz.nl deploy".split(" ")).decode()
        print(response)
        await self._gpt.tts3(response)
        return False
@BordedDev nice review so far! I will check your new files tomorrow. I'm now fighting with speech recognition and command execution. It's a cool project and it was open source for a while but then i really, really start to rape the code. But check how easy it is to add functions to the LLM: The docstrings are instructions for the LLM. I have somewhere a class inspector converting that nice to LLM commands. So, if i need new funcitonality, I just add it there. ``` class ChatFunctions: def __init__(self, gpt): self._gpt = gpt """Functions callable by GPT""" async def uptime(self,*args): """ Execute this function if it has to do with uptime of server or if use wants to know how long a server is up. :return: str """ result = subprocess.check_output(["ssh","retoor@molodetz.nl","uptime","-p","&&", "printf 'Since:'","&&","uptime","-s"]).decode() result = await self._gpt.communicate(f"Explain the uptime of this server using this data: {result}") await self._gpt.tts3(result) async def deploy(self,*args): """ Execute this function if it has to do with deploy, deployment, update, upgrade :return: str """ await self._gpt.tts3("Deploying molodetz.nl") response = subprocess.check_output("ssh molodetz.nl deploy".split(" ")).decode() print(response) await self._gpt.tts3(response) return False ```
retoor reviewed 2025-01-16 18:37:59 +00:00
@ -0,0 +6,4 @@
There is a bunch of unused code
Autosolver is broken, I had to move the function around
Owner

Ah, sad. I didn't know status of the code. But if it's working again, i will put the sudoku on my site.

Ah, sad. I didn't know status of the code. But if it's working again, i will put the sudoku on my site.
retoor reviewed 2025-01-16 18:39:29 +00:00
@ -0,0 +30,4 @@
*
* @type {(number|null|undefined)[]}
*/
#activeState = new Array(9 * 9)
Owner

Ooooh, magic numbers :P Did i have that too?

Ooooh, magic numbers :P Did i have that too?
Author
First-time contributor

Dang it I missed it, it's meant to be SUDOKU_GRID_SIZE. You did with the new Puzzle(9)

Dang it I missed it, it's meant to be `SUDOKU_GRID_SIZE`. You did with the `new Puzzle(9)`
retoor reviewed 2025-01-16 18:41:46 +00:00
retoor left a comment
Owner

Nice review so far!

Nice review so far!
Author
First-time contributor

@BordedDev nice review so far! I will check your new files tomorrow. I'm now fighting with speech recognition and command execution. It's a cool project and it was open source for a while but then i really, really start to rape the code. But check how easy it is to add functions to the LLM:

The docstrings are instructions for the LLM. I have somewhere a class inspector converting that nice to LLM commands.
So, if i need new funcitonality, I just add it there.

class ChatFunctions:

    def __init__(self, gpt):

        self._gpt = gpt

    """Functions callable by GPT"""

    async def uptime(self,*args):
        """
        Execute this function if it has to do with uptime of server or if use wants to know how long a server is up.
        
        :return: str
        """
        result = subprocess.check_output(["ssh","retoor@molodetz.nl","uptime","-p","&&", "printf 'Since:'","&&","uptime","-s"]).decode()
        result = await self._gpt.communicate(f"Explain the uptime of this server using this data: {result}")
        await self._gpt.tts3(result)

    async def deploy(self,*args):
        """
        Execute this function if it has to do with deploy, deployment, update, upgrade

        :return: str
        """
        await self._gpt.tts3("Deploying molodetz.nl")
        response = subprocess.check_output("ssh molodetz.nl deploy".split(" ")).decode()
        print(response)
        await self._gpt.tts3(response)
        return False

Interesting, I ended up creating a class with a cmd_ prefix for functions the AI can call, but I have to add the function manually to the prompt, not that it would ever call anything other img and react. For the message it matches [ img | Some image] using a handwritten parser with the intent of moving it to Lark. How do you manage it or does it directly generate the python?

^ That whole system is what I want to replace, which is why I was looking at TTS/STT since it's heavily Discord dependant with FastApi crowbarred in

> @BordedDev nice review so far! I will check your new files tomorrow. I'm now fighting with speech recognition and command execution. It's a cool project and it was open source for a while but then i really, really start to rape the code. But check how easy it is to add functions to the LLM: > > The docstrings are instructions for the LLM. I have somewhere a class inspector converting that nice to LLM commands. > So, if i need new funcitonality, I just add it there. > > > ``` > class ChatFunctions: > > def __init__(self, gpt): > > self._gpt = gpt > > """Functions callable by GPT""" > > async def uptime(self,*args): > """ > Execute this function if it has to do with uptime of server or if use wants to know how long a server is up. > > :return: str > """ > result = subprocess.check_output(["ssh","retoor@molodetz.nl","uptime","-p","&&", "printf 'Since:'","&&","uptime","-s"]).decode() > result = await self._gpt.communicate(f"Explain the uptime of this server using this data: {result}") > await self._gpt.tts3(result) > > async def deploy(self,*args): > """ > Execute this function if it has to do with deploy, deployment, update, upgrade > > :return: str > """ > await self._gpt.tts3("Deploying molodetz.nl") > response = subprocess.check_output("ssh molodetz.nl deploy".split(" ")).decode() > print(response) > await self._gpt.tts3(response) > return False > ``` Interesting, I ended up creating a class with a `cmd_` prefix for functions the AI can call, but I have to add the function manually to the prompt, not that it would ever call anything other `img` and `react`. For the message it matches `[ img | Some image]` using a handwritten parser with the intent of moving it to Lark. How do you manage it or does it directly generate the python? ^ That whole system is what I want to replace, which is why I was looking at TTS/STT since it's heavily Discord dependant with FastApi crowbarred in
BordedDev added 1 commit 2025-01-16 21:45:56 +00:00
Fixed magic number not using global
BordedDev reviewed 2025-01-16 21:49:25 +00:00
@ -0,0 +91,4 @@
||
field.row.index == this.row.index && field.value == this._value)
}).filter(field => field != this).length == 0
Author
First-time contributor

A double filter, it's inefficient (since both will loop through the array) but for the size it doesn't really matter

A double filter, it's inefficient (since both will loop through the array) but for the size it doesn't really matter
BordedDev reviewed 2025-01-16 21:51:52 +00:00
@ -0,0 +295,4 @@
this.debugEvents = true;
this.initalized = false
this.rows = []
if (isNaN(arg)) {
Author
First-time contributor

Nice usage of the coercion, even if my IDE complains because there is coercion

Nice usage of the coercion, even if my IDE complains because there is coercion
BordedDev reviewed 2025-01-16 21:59:38 +00:00
@ -0,0 +391,4 @@
this._initialized = false;
const regex = /\d/g;
const matches = [...content.matchAll(regex)]
Author
First-time contributor

Regex strikes again 🏃💨. Technically inefficient, practically I like it.

BTW it's intentionally not an array it returns so that you can efficiently stream the results through the iterator (that's how I understand the below)

With matchAll() available, you can avoid the while loop and exec with g. Instead, you get an iterator to use with the more convenient for...of, array spreading, or Array.from() constructs:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll

Regex strikes again 🏃💨. Technically inefficient, practically I like it. BTW it's intentionally not an array it returns so that you can efficiently stream the results through the iterator (that's how I understand the below) >With matchAll() available, you can avoid the while loop and exec with g. Instead, you get an iterator to use with the more convenient for...of, array spreading, or Array.from() constructs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll
BordedDev added 1 commit 2025-01-16 22:05:20 +00:00
Owner
Hey @BordedDev come talk at https://rock.molodetz.nl.
BordedDev added 1 commit 2025-01-16 23:05:10 +00:00
BordedDev added 1 commit 2025-01-16 23:11:06 +00:00
BordedDev reviewed 2025-01-16 23:17:25 +00:00
@ -0,0 +1,73 @@
:host {
display: inline-block;
--border-radius: 5px;
Author
First-time contributor

I'd normally use a @property for this but it wasn't working the initial value so I did this instead
https://developer.mozilla.org/en-US/docs/Web/CSS/@property

@property --border-radius {
  syntax: "<length>";
  inherits: true;
  initial-value: 5px;
}
I'd normally use a @property for this but it wasn't working the initial value so I did this instead https://developer.mozilla.org/en-US/docs/Web/CSS/@property ```css @property --border-radius { syntax: "<length>"; inherits: true; initial-value: 5px; } ```
BordedDev reviewed 2025-01-16 23:21:47 +00:00
@ -0,0 +3,4 @@
max = Math.floor(max)
return Math.floor(Math.random() * (max - min + 1)) + min
}
Author
First-time contributor

I ran Biome.js over this, and I'm a ; hater (my default config), that's why they're not here

I ran Biome.js over this, and I'm a `;` hater (my default config), that's why they're not here
BordedDev reviewed 2025-01-16 23:25:04 +00:00
@ -0,0 +248,4 @@
this.puzzle = puzzle
this.cols = []
this.index = this.puzzle.rows.length
const me = this
Author
First-time contributor

A lot of these me proxies aren't necessary with the arrow functions you're using, they're removed in the modified version

A lot of these `me` proxies aren't necessary with the arrow functions you're using, they're removed in the modified version
BordedDev reviewed 2025-01-16 23:28:24 +00:00
@ -0,0 +300,4 @@
return invalid[invalid.length - 1]
}
for (const row of this.rows) {
Author
First-time contributor

Switched .forEach to for of loop for performance (recommended practice, but honestly, it’s not a big deal - I'd even call it a nitpick)

Switched `.forEach` to `for of` loop for performance (recommended practice, but honestly, it’s not a big deal - I'd even call it a nitpick)
BordedDev reviewed 2025-01-16 23:29:38 +00:00
@ -0,0 +365,4 @@
const matches = [...content.matchAll(regex)]
const max = this.size * this.size
for (const [index, match] of matches.entries()) {
Author
First-time contributor

Collapsed the index to come from the matches array, since I noted the 1-to-1 relation

Collapsed the index to come from the `matches` array, since I noted the 1-to-1 relation
BordedDev added 1 commit 2025-01-16 23:35:29 +00:00
BordedDev reviewed 2025-01-16 23:40:04 +00:00
@ -0,0 +769,4 @@
} else {
const keyLookup = {
u() {
puzzle.popState()
Author
First-time contributor

This is mostly personal preference, since an "industry standard" way would be key => action enum => (action transform =>) action function. I find this a bit easier to read. If I'd bring in a library for the matching I'd have used cond from lodash https://lodash.com/docs/4.17.15#cond

This is mostly personal preference, since an "industry standard" way would be key => action enum => (action transform =>) action function. I find this a bit easier to read. If I'd bring in a library for the matching I'd have used `cond` from lodash https://lodash.com/docs/4.17.15#cond
BordedDev reviewed 2025-01-16 23:43:44 +00:00
@ -0,0 +19,4 @@
.sudoku-field {
aspect-ratio: 1 / 1;
width: 1em;
line-height: 1;
Author
First-time contributor

This is to fix the vertical alignment since it had a value of 1.5 for me

This is to fix the vertical alignment since it had a value of 1.5 for me
BordedDev reviewed 2025-01-16 23:45:30 +00:00
@ -0,0 +13,4 @@
* @param min {number}
* @param max {number}
* @returns {number}
*/
Author
First-time contributor

Type hints are here since I can see them in my IDE and make things slightly easier to work with, hence why I've put them everywhere but with no actual docs

Type hints are here since I can see them in my IDE and make things slightly easier to work with, hence why I've put them everywhere but with no actual docs
BordedDev added 1 commit 2025-01-16 23:50:45 +00:00
Removed unnecessary parameter
BordedDev reviewed 2025-01-16 23:51:24 +00:00
@ -0,0 +29,4 @@
*
* @type {SudokuState[]}
*/
#state = []
Author
First-time contributor

This contains a stack of states, essentially the initial state + all steps

This contains a stack of states, essentially the initial state + all steps
BordedDev added 1 commit 2025-01-16 23:55:38 +00:00
BordedDev reviewed 2025-01-16 23:57:32 +00:00
@ -0,0 +43,4 @@
*/
get grid() {
const gridValue = [...this.#activeState]
Object.freeze(gridValue)
Author
First-time contributor

This mainly to enforce that the underlying grid isn't modifiable, technically we return a new array anyway, so there is little point. #activeState is also always replaced so we can freeze it when it's created, but this felt more flexible

This mainly to enforce that the underlying grid isn't modifiable, technically we return a new array anyway, so there is little point. `#activeState` is also always replaced so we can freeze it when it's created, but this felt more flexible
BordedDev added 1 commit 2025-01-16 23:59:09 +00:00
BordedDev added 1 commit 2025-01-17 00:00:00 +00:00
BordedDev added 1 commit 2025-01-17 00:01:31 +00:00
BordedDev reviewed 2025-01-17 00:03:02 +00:00
@ -0,0 +75,4 @@
for (const char of serializedState) {
if (PARSE_RANGE_START <= char && char <= PARSE_RANGE_END) {
if (VALID_RANGE_START <= char && char <= PARSE_RANGE_END) {
Author
First-time contributor

Since 0 is an invalid value for the SudokuState this allows for them to be filtered out while still increasing the number placement

Since 0 is an invalid value for the `SudokuState` this allows for them to be filtered out while still increasing the number placement
BordedDev reviewed 2025-01-17 00:05:39 +00:00
@ -0,0 +95,4 @@
* @returns {undefined|number}
*/
#sampleRandomEmptyField(stateToSample = this.#activeState) {
const iters = [...stateToSample.entries().filter(([, v]) => v == null)]
Author
First-time contributor

Recent learned that the variables are optional in destruction, had them as _ before.

Recent learned that the variables are optional in destruction, had them as `_` before.
BordedDev reviewed 2025-01-17 00:08:46 +00:00
@ -0,0 +126,4 @@
const left = Math.trunc(Math.trunc(slot % 9) / 3) * 3
const top = Math.trunc(Math.trunc(slot / 9) / 3) * (9 * 3)
for (let y = 0; y <= 27; y += 9) {
Author
First-time contributor

I'm being a little cheeky here, making the multiplication an addition instead. I can see an argument that this should be a 1 to 3 loop with multiplication, I liked the simplicity of positionIndex this way

I'm being a little cheeky here, making the multiplication an addition instead. I can see an argument that this should be a 1 to 3 loop with multiplication, I liked the simplicity of `positionIndex` this way
BordedDev reviewed 2025-01-17 00:09:57 +00:00
@ -0,0 +184,4 @@
* @param state {SudokuState}
* @return {Set<number>}
*/
getValues(slot, state = this.#activeState) {
Author
First-time contributor

These return a Set to simplify/combine the logic for automatically resolving and checking if a number is valid. In theory, the check would be faster since it can check early

These return a `Set` to simplify/combine the logic for automatically resolving and checking if a number is valid. In theory, the check would be faster since it can check early
BordedDev added 1 commit 2025-01-17 00:12:17 +00:00
BordedDev reviewed 2025-01-17 00:14:37 +00:00
@ -0,0 +268,4 @@
}
class SudokuHost extends HTMLElement {
static observedAttributes = ["size", "puzzle", "readonly"]
Author
First-time contributor

These were necessary for me to have attributeChangedCallback work

These were necessary for me to have `attributeChangedCallback` work
BordedDev reviewed 2025-01-17 00:17:52 +00:00
@ -0,0 +304,4 @@
#keyProcessors = new Map()
get isActive() {
return this.matches(":hover")
Author
First-time contributor

This was more convenient for me than the mouseenter/mouseexit events

This was more convenient for me than the mouseenter/mouseexit events
BordedDev added 1 commit 2025-01-17 00:20:28 +00:00
BordedDev added 1 commit 2025-01-17 00:21:59 +00:00
BordedDev reviewed 2025-01-17 00:24:03 +00:00
@ -0,0 +425,4 @@
this.#styling = document.createElement("link")
this.#styling.rel = "stylesheet"
this.#styling.href = "sudoku.rewrite.css"
this.#root.appendChild(this.#styling)
Author
First-time contributor

This was more convenient for me than replacing the outer HTML with a <style>...</style> string to get syntax highlighting

This was more convenient for me than replacing the outer HTML with a `<style>...</style>` string to get syntax highlighting
BordedDev force-pushed main from 30062089f6 to 77e78c8031 2025-01-17 00:38:17 +00:00 Compare
BordedDev changed title from WIP: Sudoku review to Sudoku review 2025-01-17 00:41:19 +00:00
This pull request can be merged automatically.
You are not authorized to merge this pull request.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u main:BordedDev-main
git checkout BordedDev-main
Sign in to join this conversation.
No reviewers
No Label
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: retoor/sudoku#1
No description provided.