memos icon indicating copy to clipboard operation
memos copied to clipboard

Offline capabilities to Memos

Open Cadavanaugh opened this issue 7 months ago • 1 comments

Describe the solution you'd like

It would be interesting if when we lost internet connection we still had the app running normally doing local changes and then when back online sync thoses changes with the server.

Type of feature

User Experience (UX)

Additional context

This video might help with the development https://www.youtube.com/watch?v=1FMkFc4TIio

Cadavanaugh avatar May 10 '25 17:05 Cadavanaugh

related: I'd like for drafts to get synced as they get typed to avoid losing them - even if just offline

dzervas avatar May 20 '25 20:05 dzervas

I took a stab at this, but it is complicated by the fact that Memos uses gRPC-Web. gRPC-Web uses POST requests for everything which can't be cached in the native Cache objects without some manipulation. It looks like the server already exposes a REST API, so maybe we could switch to that?

asavageiv avatar Sep 11 '25 20:09 asavageiv

I tried generating an API client using @openapitools/openapi-generator-cli and the openapi.yaml but some of the methods don't work correctly. For example GetWorkspaceSetting should take a string like "workspace/settings/GENERAL" and make a request to /api/v1/workspace/settings/GENERAL but instead it makes a request to /api/v1/workspace/workspace%2Fsettings%2FGENERAL/*. Note the URL encoding and trailing /*.

It seems like an issue with how the proto definition is translated to openapi.yaml.

  rpc GetWorkspaceSetting(GetWorkspaceSettingRequest) returns (WorkspaceSetting) {
    option (google.api.http) = {get: "/api/v1/{name=workspace/settings/*}"};
    option (google.api.method_signature) = "name";
  }

generates

/api/v1/workspace/{workspace}/*:
        get:
            tags:
                - WorkspaceService
            description: Gets a workspace setting.
            operationId: WorkspaceService_GetWorkspaceSetting
            parameters:
                - name: workspace
                  in: path
                  description: The workspace id.
                  required: true
                  schema:
                    type: string

Maybe a bug in protoc-gen-openapi

asavageiv avatar Sep 11 '25 23:09 asavageiv

I found the issue. protoc-gen-openai expects the path pattern to have alternating names and stars per this comment:

 // The starred path is assumed to be in the form "things/*/otherthings/*". 
 // We want to convert it to "things/{thingsId}/otherthings/{otherthingsId}". 

https://github.com/google/gnostic/blob/e0e09f70628157dc0db87ed60a109465ae62723b/cmd/protoc-gen-openapi/generator/generator.go#L508-L516

But our path looks like workspace/settings/*. This is why we're having to use google.api.method_signature and having to prepend /workspace/settings/ in the TS, because the generated path is wrong. I think if we change the proto to match the expectations, we'll get better results:

  // Gets a workspace setting.
  rpc GetWorkspaceSetting(GetWorkspaceSettingRequest) returns (WorkspaceSetting) {
    option (google.api.http) = {get: "/api/v1/workspace/{name=settings/*}"};
    option (google.api.method_signature) = "name";
  }

This matches the example here: https://github.com/googleapis/googleapis/blob/f8776fec04e336527ba7279d960105533a1c4e21/google/api/http.proto#L68

asavageiv avatar Sep 12 '25 00:09 asavageiv

@boojack Maybe you can lend some insight here. Is there a reason for including both workspace and settings in the name? I'm guessing that is trying to follow AIP guidance, but we're not following the recommendation of alternating collection/id/collection/id per https://google.aip.dev/122#guidance

I want to get the openapi.yaml correct so I can get offline mode working.

asavageiv avatar Sep 12 '25 02:09 asavageiv

https://github.com/google/gnostic/pull/458 attempts a fix upstream, but I'm not sure it will be accepted

asavageiv avatar Sep 12 '25 12:09 asavageiv