Bike 2.0 (Preview 270)

  • Added Help menu items
  • Improved Commands Explorer UI
  • Improved Markdown open/import UI
  • Fixed sidebar selection drawing
  • Fixed over-escaping in markdown export
  • Fixed blank lines lost during Bike Markdown round-trip
  • Fixed ordered list numbering reset after blank lines
  • Improved drag and drop performance in sidebar and editor

Extensions

  • Added new system fill colors

Download:

4 Likes

Keep sending crash reports! Please :slight_smile:

I hide my menu bar, and this is what i now see with 270 (wasn’t like this with 269)

i think this is a bug

if i move down on row 2, then it moves down

if i move up on row 2, to put it above row 1, row 2 parents everything below it

I’m not able to reproduce this yet. You were remapping commands… what’s the exact command that you are using for “move up” and “move down”?

I just noticed that View > Hide / Show Interface appears somewhat broken… I think it maybe a new problem caused by macOS 26.4. I just updated and I think it was working on previous OS. Anyway, I’ll look into it.

Well that’s weird looking. You’ve probably told me many times, but what OS version are you on? I’m not able to reproduce this on macOS 26.4.

this happened when i had the new keybinding set (as in the screenshot i set earlier). now that i have removed the keybinding, it doesn’t happen.

macOS 26.4 (25E246)

huh, it’s gone. chalk it up to a ghost

Hey!

There seems to be a repeatable crash in 270 with regex-based search and replace when the replacement tries to use an empty capture group.

Steps to reproduce

  • Open a new Bike document
  • Add a single row containing A
  • Open Search & Replace
  • Enable regex
  • Enter a regex of A(\w+)
  • Enter a replacement of $1
1 Like

An extension-kit question:

Is the intention to target a very early version of JavaScriptCore (perhaps for compatibility) ?

At build of a module in which I have used the regular expression u flag: I am getting the message:

TS1501: This regular expression flag is only available 
when targeting 'es6' or later.

Is it safe for me to add a post es6 value for compilerOptions.target in a tsconfig ?

Another extension-kit question, about icons for inspector tabs:

In inspector.d.ts we have:

export type InspectorItem = {
  /** Label shown in the tab bar tooltip. */
  label: string
  /** The DOM script to run. */
  script: DOMScript
}

Can we also specify (in the extension) an icon to show for a particular inspector tab, or should we do that manually through the GUI Interface Explorer ?

This should be done through Interface Explorer.

1 Like

No, I only plan to support back to macOS 15.

Can you show me the code causing the problem. I tried this and it seems to work:

const regex = /\u{1F600}/u; 
console.log(regex.test("😀"));

I think yes, we just need to make sure to test whatever the feature is and ensure it works on macOS 15.

In the dom directory of a .bkext, I have a parser.ts file with the contents below (still in progress) which is imported in an inspector.tsx file with a line like:

import { parsePandocString } from './parser';

Expand disclosure triangle to view TS source
const VALID_ROW_TYPES = [
    'body', 'heading', 'quote', 'code', 'note',
    'unordered', 'ordered', 'task', 'hr'
];

// Generic Either types
type Either<L, R> = { type: "Either"; Left: L } | { type: "Either"; Right: R };
const Left = <L>(x: L): Either<L, never> => ({ type: "Either", Left: x });
const Right = <R>(x: R): Either<never, R> => ({ type: "Either", Right: x });
export const bindLR = <L, R>(lr: Either<L, R>) => <R2>(mf: (r: R) => Either<L, R2>): Either<L, R2> =>
    "Left" in lr ? Left(lr.Left) : mf(lr.Right);

// String helpers
const words = (s: string) => s.split(/\s+/u).filter(Boolean);

// Validators
const validCustomDataKeyLR = (k: string): Either<string, string> => {
    const m = k.match(/[^a-z\-]+/u);
    return m ? Left(`Unexpected '${m.join("")}' in '${k}' (use lower a-z and hyphens)`) : Right(k);
};

const validNameLR = (isClass: boolean) => (s: string): Either<string, string> => {
    const [prefix, type] = isClass ? [".", "class"] : ["#", "identifier"];
    if ((/\s/u).test(s)) return Left(`Invalid ${type} name (contains whitespace): '${s}'`);
    const m = s.match(/[^a-zA-Z0-9\-_]+/u);
    if (m) return Left(`Unexpected '${m.join("")}' in ${type} name '${s}'`);
    if (!(/^[A-Za-z]/u).exec(s)) return Left(`Expected letter after '${prefix}' in ${type}: '${s}'`);
    return Right(s);
};

const validPrefixCharLR = (token: string): Either<string, string> => {
    const prefix = token[0];
    return ".#".includes(prefix) ? Right(prefix) : Left(`Expected '.' or '#'. Saw '${prefix}'`);
};

// Token Parsers
const readNameLR = (token: string): Either<string, [string, string]> =>
    1 < token.length
        ? bindLR(validPrefixCharLR(token))((prefix: string) =>
            bindLR(validNameLR("." === prefix)(token.slice(1)))((name: any) => Right([prefix, name]))
        )
        : Left(`No name seen after prefix '${token}'`);

const readKVLR = (token: string): Either<string, [string, string]> => {
    const parts = token.split("=");
    if (parts.length !== 2) {
        return Left(`Expected single "=", saw ${parts.length - 1} in ${token}`);
    }

    const k = parts[0];
    const v = parts[1].replace(/[\'\"]+/ug, "");

    // Intercept 'type' and validate against Bike's RowType API
    if (k === "type" && !VALID_ROW_TYPES.includes(v)) {
        return Left(`Invalid type '${v}'. Allowed: ${VALID_ROW_TYPES.join(", ")}`);
    }

    return bindLR(validCustomDataKeyLR(k))(validK => Right([validK, v]));
};

// Main Parser Export
export const parsePandocString = (s: string): Either<string, Record<string, any>> => {
    const tokens = words(s.replace(/^[\{\s]+/, "").replace(/[\}\s]+$/u, ""));
    const kvs: [string, string][] = [];

    for (const token of tokens) {
        const result = token.includes("=") ? readKVLR(token) : readNameLR(token);
        if ("Left" in result) return result; // Fail fast on first error
        kvs.push(result.Right);
    }

    const dict = kvs.reduce((acc: any, [k, v]) => ({
        ...acc,
        [k]: "." === k ? (acc[k] || []).concat(v) : v
    }), {});

    return Right(dict);
};

at build time, unless I remove all the /../u flags, I see:

Expand disclosure triangle to view messages

src/data-inspector.bkext/dom/parser.ts:14:43 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.

14 const words = (s: string) => s.split(/\s+/u).filter(Boolean);
                                             ~
src/data-inspector.bkext/dom/parser.ts:18:34 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.

18     const m = k.match(/[^a-z\-]+/u);
                                    ~
src/data-inspector.bkext/dom/parser.ts:24:14 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.

24     if ((/\s/u).test(s)) return Left(`Invalid ${type} name (contains whitespace): '${s}'`);
                ~
src/data-inspector.bkext/dom/parser.ts:25:41 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.

25     const m = s.match(/[^a-zA-Z0-9\-_]+/u);
                                           ~
src/data-inspector.bkext/dom/parser.ts:27:22 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.

27     if (!(/^[A-Za-z]/u).exec(s)) return Left(`Expected letter after '${prefix}' in ${type}: '${s}'`);
                        ~
src/data-inspector.bkext/dom/parser.ts:51:41 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.

51     const v = parts[1].replace(/[\'\"]+/ug, "");
                                           ~
src/data-inspector.bkext/dom/parser.ts:63:70 - error TS1501: This regular expression flag is only available when targeting 'es6' or later.

63     const tokens = words(s.replace(/^[\{\s]+/, "").replace(/[\}\s]+$/u, ""));                                        ~

Perhaps parser.ts should import a missing context ?

Interesting… and odd!

When I include and build that parser code I see no errors at build time. On the other hand I do get an error at runtime (shows in Bike’s Logs Explorer). That error is that /[\'\"]+/ug is not a valid Javascript regular expression and instead needs to be /['"]+/ug. Once I do that I get no errors at build or runtime.


How are you building. Can you show me terminal command and exact output you are seeing?

Part of that oddity (['"]) may arise from the fact that I had taken out all the /u and put them back a bit roughly with search and replace, but more broadly, what I am doing and seeing is:

Hi, and thanks. I didn’t get to this for 271, but am looking at it now, but can’t seem to reproduce it. Generally if my document only has an “A” I get no matches to replace. Are you still able to reproduce this in 271?

Sorry, I must have fuzzed out and put in the wrong regex to reproduce the issue. :blush:

Also, I can reduce the steps to reproduce even further:

  • Open a new Bike document
  • Add a single row containing A
  • Open Search & Replace
  • Enable regex
  • Enter a regex of A(\w)*
  • Press [All]

Crashes every time.

Video of behavior at https://drive.google.com/file/d/1tVPN6GFy7S5reyJrrw3V1omqtHfQYUK5/view